├── docs ├── assets │ ├── .gitkeep │ ├── favicon.png │ ├── img │ │ ├── bomb.png │ │ ├── cow.png │ │ ├── five.png │ │ ├── fly.png │ │ ├── four.png │ │ ├── goat.png │ │ ├── mail.png │ │ ├── one.png │ │ ├── two.png │ │ ├── blast.png │ │ ├── clock.png │ │ ├── snake.png │ │ └── three.png │ ├── lo-logo.png │ ├── toppy-logo.png │ ├── toppy-favicon.png │ ├── favicon │ │ ├── favicon.ico │ │ ├── og-image.jpg │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-150x150.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-256x256.png │ │ └── safari-pinned-tab.svg │ ├── toppy-logo-white.png │ ├── icons │ │ ├── icons8-beer-50.png │ │ ├── icons8-cafe-50.png │ │ ├── icons8-cola-50.png │ │ ├── icons8-pears-50.png │ │ ├── icons8-pizza-50.png │ │ ├── icons8-watch-50.png │ │ ├── icons8-orange-50.png │ │ ├── icons8-scooter-50.png │ │ ├── icons8-whiskey-50.png │ │ ├── icons8-cocktail-50.png │ │ ├── icons8-comments-50.png │ │ ├── icons8-confetti-50.png │ │ ├── icons8-dynamite-50.png │ │ ├── icons8-explosive-50.png │ │ ├── icons8-hamburger-50.png │ │ ├── icons8-sandwich-50.png │ │ ├── icons8-watermelon-50.png │ │ ├── icons8-french-fries-50.png │ │ ├── icons8-sweet-banana-50.png │ │ ├── icons8-tequila-shot-50.png │ │ ├── icons8-fried-chicken-50.png │ │ └── icons8-firework-explosion-100.png │ └── archived-versions.json ├── styles │ ├── base │ │ ├── _typography.scss │ │ ├── _buttons.scss │ │ ├── __index.scss │ │ ├── _fonts.scss │ │ ├── _pre.scss │ │ ├── _input.scss │ │ ├── _utility.scss │ │ ├── _reset.scss │ │ └── _icons.scss │ ├── browsers │ │ ├── _ie11.scss │ │ ├── __index.scss │ │ ├── _firefox.scss │ │ └── _chrome.scss │ ├── layouts │ │ └── __index.scss │ ├── responsive │ │ ├── _lg.scss │ │ ├── _md.scss │ │ ├── __index.scss │ │ └── _xl.scss │ ├── helpers │ │ ├── functions │ │ │ ├── _z.scss │ │ │ ├── __index.scss │ │ │ ├── _fs.scss │ │ │ ├── _clr.scss │ │ │ └── _rem.scss │ │ ├── mixins │ │ │ ├── __index.scss │ │ │ ├── _acceleration.scss │ │ │ ├── _placeholder.scss │ │ │ ├── _smooth-font.scss │ │ │ ├── _ellipsis.scss │ │ │ └── _gradient.scss │ │ ├── __index.scss │ │ └── _debug.scss │ ├── animations │ │ ├── __index.scss │ │ └── _slide_in.scss │ ├── pages │ │ └── __index.scss │ ├── components │ │ ├── _table.scss │ │ ├── _menu.scss │ │ ├── __index.scss │ │ ├── _hero.scss │ │ ├── _controls.scss │ │ ├── _blade.scss │ │ ├── _modal.scss │ │ └── _target-element-container.scss │ ├── icons │ │ ├── toppy.ttf │ │ └── toppy.woff │ ├── vendors │ │ ├── __index.scss │ │ ├── _pretty-checkbox.scss │ │ ├── _prism.scss │ │ └── _bootstrap.scss │ ├── elements │ │ ├── __index.scss │ │ ├── _btn.scss │ │ ├── _toast.scss │ │ ├── _hr.scss │ │ └── _tooltip.scss │ ├── _common.scss │ ├── root.scss │ └── _config.scss ├── app │ ├── utils │ │ ├── content │ │ │ ├── content.component.scss │ │ │ └── content.component.ts │ │ ├── sub-section │ │ │ ├── sub-section.component.html │ │ │ └── sub-section.component.ts │ │ ├── section │ │ │ ├── section.component.html │ │ │ └── section.component.ts │ │ └── scollspy.directive.ts │ ├── examples │ │ ├── modal-example │ │ │ ├── modal-example.component.scss │ │ │ ├── modal-example.component.html │ │ │ └── modal-example.component.ts │ │ ├── ribbon-example │ │ │ ├── ribbon-example.component.scss │ │ │ ├── ribbon-example.component.html │ │ │ └── ribbon-example.component.ts │ │ ├── control-example │ │ │ ├── control-example.component.scss │ │ │ ├── control-example.component.html │ │ │ └── control-example.component.ts │ │ ├── dropdown-example │ │ │ ├── dropdown-example.component.scss │ │ │ ├── dropdown-example.component.html │ │ │ └── dropdown-example.component.ts │ │ ├── dynamic-text-example │ │ │ ├── dynamic-text-example.component.html │ │ │ └── dynamic-text-example.component.ts │ │ ├── fullscreen-position-example │ │ │ ├── fullscreen-position-example.component.html │ │ │ └── fullscreen-position-example.component.ts │ │ ├── drag-example │ │ │ ├── drag-example.component.html │ │ │ └── drag-example.component.ts │ │ ├── slide-position-example │ │ │ ├── slide-position-example.component.html │ │ │ └── slide-position-example.component.ts │ │ ├── global-position-example │ │ │ ├── global-position-example.component.html │ │ │ └── global-position-example.component.ts │ │ └── relative-position-example │ │ │ ├── relative-position-example.component.html │ │ │ └── relative-position-example.component.ts │ ├── host-components │ │ ├── tooltip │ │ │ ├── tooltip.component.html │ │ │ └── tooltip.component.ts │ │ ├── simple-modal │ │ │ ├── simple-modal.component.html │ │ │ └── simple-modal.component.ts │ │ ├── simple-list │ │ │ ├── simple-list.component.html │ │ │ └── simple-list.component.ts │ │ └── hero-screen │ │ │ ├── hero-screen.component.ts │ │ │ └── hero-screen.component.html │ ├── test │ │ ├── test.component.html │ │ ├── test.component.ts │ │ ├── test.component.scss │ │ └── test.component.spec.ts │ ├── codes.ts │ ├── app.component.spec.ts │ ├── app.module.ts │ ├── app.component.ts │ └── app.component.html ├── environments │ ├── version.ts │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── tsconfig.app.json ├── tsconfig.spec.json ├── tslint.json ├── main.ts ├── browserslist ├── test.ts ├── karma.conf.js ├── index.html └── polyfills.ts ├── commitlint.config.js ├── .prettierrc ├── scripts ├── version.sh ├── gh-pages.js ├── archive.js └── build-md.js ├── .stylelintrc ├── projects └── toppy │ ├── ng-package.json │ ├── src │ ├── lib │ │ ├── position │ │ │ ├── index.ts │ │ │ ├── fullscreen-position.ts │ │ │ ├── position.ts │ │ │ ├── slide-position.ts │ │ │ ├── global-position.ts │ │ │ └── relative-position.ts │ │ ├── toppy.module.ts │ │ ├── config.ts │ │ ├── styles.scss │ │ ├── template.html │ │ ├── utils.ts │ │ ├── toppy.ts │ │ ├── models.ts │ │ ├── toppy.component.ts │ │ └── toppy-control.ts │ ├── public_api.ts │ ├── tests │ │ ├── positions │ │ │ ├── fullscreen-position.spec.ts │ │ │ ├── slide-position.spec.ts │ │ │ ├── global-position.spec.ts │ │ │ └── relative-position.spec.ts │ │ ├── utils.spec.ts │ │ ├── toppy.spec.ts │ │ └── toppy.component.spec.ts │ └── test.ts │ ├── tslint.json │ ├── tsconfig.spec.json │ ├── package.json │ ├── tsconfig.lib.json │ └── karma.conf.js ├── e2e ├── src │ ├── app.po.ts │ └── app.e2e-spec.ts ├── tsconfig.e2e.json └── protractor.conf.js ├── .editorconfig ├── tsconfig.json ├── .gitignore ├── LICENSE ├── .travis.yml ├── .releaserc ├── tslint.json ├── package.json ├── angular.json └── CHANGELOG.md /docs/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/styles/base/_typography.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/styles/browsers/_ie11.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/styles/layouts/__index.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/styles/responsive/_lg.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/styles/responsive/_md.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/styles/helpers/functions/_z.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/app/utils/content/content.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/app/examples/modal-example/modal-example.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/app/examples/ribbon-example/ribbon-example.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/styles/animations/__index.scss: -------------------------------------------------------------------------------- 1 | @import './slide_in'; 2 | -------------------------------------------------------------------------------- /docs/styles/pages/__index.scss: -------------------------------------------------------------------------------- 1 | // @import "./playground"; 2 | -------------------------------------------------------------------------------- /docs/app/examples/control-example/control-example.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/app/examples/dropdown-example/dropdown-example.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/environments/version.ts: -------------------------------------------------------------------------------- 1 | export const TOPPY_VERSION='2.3.4' 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /docs/styles/components/_table.scss: -------------------------------------------------------------------------------- 1 | table { 2 | // @include .table; 3 | width: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /docs/styles/responsive/__index.scss: -------------------------------------------------------------------------------- 1 | @import './lg'; 2 | @import './xl'; 3 | @import './md'; 4 | -------------------------------------------------------------------------------- /docs/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon.png -------------------------------------------------------------------------------- /docs/assets/img/bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/bomb.png -------------------------------------------------------------------------------- /docs/assets/img/cow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/cow.png -------------------------------------------------------------------------------- /docs/assets/img/five.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/five.png -------------------------------------------------------------------------------- /docs/assets/img/fly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/fly.png -------------------------------------------------------------------------------- /docs/assets/img/four.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/four.png -------------------------------------------------------------------------------- /docs/assets/img/goat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/goat.png -------------------------------------------------------------------------------- /docs/assets/img/mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/mail.png -------------------------------------------------------------------------------- /docs/assets/img/one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/one.png -------------------------------------------------------------------------------- /docs/assets/img/two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/two.png -------------------------------------------------------------------------------- /docs/assets/lo-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/lo-logo.png -------------------------------------------------------------------------------- /docs/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /docs/app/host-components/tooltip/tooltip.component.html: -------------------------------------------------------------------------------- 1 |

2 | tooltip works! 3 |

-------------------------------------------------------------------------------- /docs/assets/img/blast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/blast.png -------------------------------------------------------------------------------- /docs/assets/img/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/clock.png -------------------------------------------------------------------------------- /docs/assets/img/snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/snake.png -------------------------------------------------------------------------------- /docs/assets/img/three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/img/three.png -------------------------------------------------------------------------------- /docs/assets/toppy-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/toppy-logo.png -------------------------------------------------------------------------------- /docs/styles/browsers/__index.scss: -------------------------------------------------------------------------------- 1 | @import './ie11'; 2 | @import './chrome'; 3 | @import './firefox'; 4 | -------------------------------------------------------------------------------- /docs/styles/icons/toppy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/styles/icons/toppy.ttf -------------------------------------------------------------------------------- /docs/assets/toppy-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/toppy-favicon.png -------------------------------------------------------------------------------- /docs/styles/icons/toppy.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/styles/icons/toppy.woff -------------------------------------------------------------------------------- /docs/assets/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon/favicon.ico -------------------------------------------------------------------------------- /docs/assets/favicon/og-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon/og-image.jpg -------------------------------------------------------------------------------- /docs/assets/toppy-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/toppy-logo-white.png -------------------------------------------------------------------------------- /docs/styles/vendors/__index.scss: -------------------------------------------------------------------------------- 1 | @import './bootstrap'; 2 | @import './pretty-checkbox'; 3 | @import './prism'; 4 | -------------------------------------------------------------------------------- /docs/styles/elements/__index.scss: -------------------------------------------------------------------------------- 1 | @import './btn'; 2 | @import './hr'; 3 | @import './tooltip'; 4 | @import './toast'; 5 | -------------------------------------------------------------------------------- /docs/styles/helpers/functions/__index.scss: -------------------------------------------------------------------------------- 1 | @import './clr'; 2 | @import './fs'; 3 | @import './rem'; 4 | @import './z'; 5 | -------------------------------------------------------------------------------- /docs/styles/helpers/functions/_fs.scss: -------------------------------------------------------------------------------- 1 | @function fs($key: 'md') { 2 | @return map-get($toppy--font-sizes, $key); 3 | } 4 | -------------------------------------------------------------------------------- /docs/app/host-components/simple-modal/simple-modal.component.html: -------------------------------------------------------------------------------- 1 |
2 | Globally positioned! 3 |
-------------------------------------------------------------------------------- /docs/assets/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /docs/assets/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-beer-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-beer-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-cafe-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-cafe-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-cola-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-cola-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-pears-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-pears-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-pizza-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-pizza-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-watch-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-watch-50.png -------------------------------------------------------------------------------- /docs/styles/helpers/functions/_clr.scss: -------------------------------------------------------------------------------- 1 | @function clr($key: 'primary') { 2 | @return map-get($toppy--colors, $key); 3 | } 4 | -------------------------------------------------------------------------------- /docs/assets/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-orange-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-orange-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-scooter-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-scooter-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-whiskey-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-whiskey-50.png -------------------------------------------------------------------------------- /docs/styles/elements/_btn.scss: -------------------------------------------------------------------------------- 1 | .btn.btn-transparent { 2 | background-color: transparent; 3 | border-color: transparent; 4 | } 5 | -------------------------------------------------------------------------------- /docs/assets/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-cocktail-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-cocktail-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-comments-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-comments-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-confetti-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-confetti-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-dynamite-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-dynamite-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-explosive-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-explosive-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-hamburger-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-hamburger-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-sandwich-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-sandwich-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-watermelon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-watermelon-50.png -------------------------------------------------------------------------------- /docs/styles/browsers/_firefox.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable function-url-quotes*/ 2 | @-moz-document url-prefix() { 3 | /* empty */ 4 | } 5 | -------------------------------------------------------------------------------- /docs/styles/responsive/_xl.scss: -------------------------------------------------------------------------------- 1 | @include media-breakpoint-up(xl) { 2 | html { 3 | // font-size: $toppy--base-font-size-xl; 4 | } 5 | } -------------------------------------------------------------------------------- /docs/app/test/test.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Hello Everyone!

3 | close 4 |
5 | -------------------------------------------------------------------------------- /docs/assets/icons/icons8-french-fries-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-french-fries-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-sweet-banana-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-sweet-banana-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-tequila-shot-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-tequila-shot-50.png -------------------------------------------------------------------------------- /docs/app/examples/control-example/control-example.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/app/host-components/simple-list/simple-list.component.html: -------------------------------------------------------------------------------- 1 |

Slide example

2 | -------------------------------------------------------------------------------- /docs/assets/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/assets/favicon/android-chrome-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/favicon/android-chrome-256x256.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-fried-chicken-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-fried-chicken-50.png -------------------------------------------------------------------------------- /docs/assets/icons/icons8-firework-explosion-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokesh-coder/toppy/HEAD/docs/assets/icons/icons8-firework-explosion-100.png -------------------------------------------------------------------------------- /docs/app/utils/sub-section/sub-section.component.html: -------------------------------------------------------------------------------- 1 |
2 |
{{ heading }}
3 | 4 |
5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "useTabs": false, 5 | "tabWidth": 2, 6 | "semi": true, 7 | "bracketSpacing": true 8 | } 9 | -------------------------------------------------------------------------------- /docs/styles/components/_menu.scss: -------------------------------------------------------------------------------- 1 | ul.comp.menu { 2 | li { 3 | background-color: transparent; 4 | border: none; 5 | color: clr('heading'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "The version would be: $1" 3 | echo "export const TOPPY_VERSION='$1'" > ./docs/environments/version.ts 4 | echo "Version file updated!" 5 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-recommended-scss", 3 | "rules": { 4 | "selector-type-no-unknown": [true, { "ignoreTypes": ["/choosy-/"] }] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/styles/elements/_toast.scss: -------------------------------------------------------------------------------- 1 | .el.toast { 2 | background: #76cc7a; 3 | padding: 1rem; 4 | border: 1px solid #7dbf81; 5 | border-radius: 3px; 6 | color: #fff; 7 | } 8 | -------------------------------------------------------------------------------- /docs/styles/helpers/mixins/__index.scss: -------------------------------------------------------------------------------- 1 | @import './acceleration'; 2 | @import './ellipsis'; 3 | @import './placeholder'; 4 | @import './smooth-font'; 5 | @import './gradient'; 6 | -------------------------------------------------------------------------------- /docs/styles/_common.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable scss/at-import-no-partial-leading-underscore */ 2 | @import './config'; 3 | @import './helpers/_index'; 4 | @import './themes/_index'; 5 | -------------------------------------------------------------------------------- /docs/app/utils/section/section.component.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ heading }}

3 | 4 |
5 | -------------------------------------------------------------------------------- /docs/styles/base/_buttons.scss: -------------------------------------------------------------------------------- 1 | button.btn.-is-plain { 2 | background-color: transparent; 3 | border-color: transparent; 4 | } 5 | 6 | button.btn.-is-compact { 7 | padding: 0; 8 | } 9 | -------------------------------------------------------------------------------- /docs/styles/helpers/__index.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable scss/at-import-no-partial-leading-underscore */ 2 | @import './functions/_index'; 3 | @import './mixins/_index'; 4 | @import './debug'; 5 | -------------------------------------------------------------------------------- /docs/styles/components/__index.scss: -------------------------------------------------------------------------------- 1 | @import 'target-element-container'; 2 | @import 'controls'; 3 | @import 'blade'; 4 | @import 'hero'; 5 | @import 'menu'; 6 | @import 'modal'; 7 | @import 'table'; 8 | -------------------------------------------------------------------------------- /docs/styles/helpers/mixins/_acceleration.scss: -------------------------------------------------------------------------------- 1 | @mixin hardware($backface: true, $perspective: 1000) { 2 | @if $backface { 3 | backface-visibility: hidden; 4 | } 5 | perspective: $perspective; 6 | } 7 | -------------------------------------------------------------------------------- /projects/toppy/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/toppy", 4 | "lib": { 5 | "entryFile": "src/public_api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /docs/styles/elements/_hr.scss: -------------------------------------------------------------------------------- 1 | .doc-el-hr { 2 | background-color: transparentize(#fff, 0.8); 3 | // height: 0.1rem; 4 | height: 1px; 5 | &.hr-dark{ 6 | background-color: transparentize(#000, 0.9); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/styles/base/__index.scss: -------------------------------------------------------------------------------- 1 | @import './fonts'; 2 | @import './icons'; 3 | @import './reset'; 4 | @import './typography'; 5 | @import './utility'; 6 | @import './input'; 7 | @import './buttons'; 8 | @import './pre'; 9 | -------------------------------------------------------------------------------- /docs/app/examples/dynamic-text-example/dynamic-text-example.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /docs/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /docs/styles/base/_fonts.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Roboto+Mono:400,500,700'); 2 | @import url('https://use.typekit.net/uur7gsm.css'); 3 | @import url('https://fonts.googleapis.com/css?family=Rubik:300,400,500,700'); 4 | -------------------------------------------------------------------------------- /docs/styles/elements/_tooltip.scss: -------------------------------------------------------------------------------- 1 | .el.tooltip { 2 | background: #e75676bd; 3 | padding: 0.3rem 0.5rem; 4 | border-radius: 3px; 5 | font-size: 11px; 6 | color: #fff; 7 | text-shadow: 1px 1px 1px #a55b6b; 8 | margin: 0; 9 | } 10 | -------------------------------------------------------------------------------- /docs/app/examples/fullscreen-position-example/fullscreen-position-example.component.html: -------------------------------------------------------------------------------- 1 |
2 |
Open fullscreen demo
3 |
4 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/position/index.ts: -------------------------------------------------------------------------------- 1 | export { FullscreenPosition } from './fullscreen-position'; 2 | export { GlobalPosition } from './global-position'; 3 | export { RelativePosition } from './relative-position'; 4 | export { SlidePosition } from './slide-position'; 5 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/toppy/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [true, "attribute", "lib", "camelCase"], 5 | "component-selector": [true, "element", "lib", "kebab-case"], 6 | "curly": [true, "ignore-same-line"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/styles/helpers/_debug.scss: -------------------------------------------------------------------------------- 1 | @if ($toppy--debug) { 2 | div, 3 | ul { 4 | box-shadow: 0px 0px 1px 0px #e3505a; 5 | } 6 | section { 7 | box-shadow: 0px 0px 1px 1px #187fe5; 8 | } 9 | 10 | span, 11 | li { 12 | box-shadow: 0px 0px 1px 0px #212529; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/styles/helpers/functions/_rem.scss: -------------------------------------------------------------------------------- 1 | @function rem($pixels, $context: $toppy--base-font-size) { 2 | @if (unitless($pixels)) { 3 | $pixels: $pixels * 1px; 4 | } 5 | @if (unitless($context)) { 6 | $context: $context * 1px; 7 | } 8 | @return $pixels / $context * 1rem; 9 | } 10 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /docs/styles/base/_pre.scss: -------------------------------------------------------------------------------- 1 | markdown .inline-code { 2 | padding: 1rem; 3 | pre[class*='language-'] { 4 | display: inline-block; 5 | line-height: 1; 6 | padding: 0; 7 | margin: 0; 8 | background: transparent; 9 | code { 10 | font-size: 1rem; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /docs/app/utils/content/content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-content', 5 | templateUrl: './content.component.html' 6 | }) 7 | export class ContentComponent implements OnInit { 8 | constructor() {} 9 | 10 | ngOnInit() {} 11 | } 12 | -------------------------------------------------------------------------------- /docs/styles/base/_input.scss: -------------------------------------------------------------------------------- 1 | input.form-control { 2 | font-weight: 500; 3 | font-size: 1.25rem; 4 | border: 0; 5 | padding: 0.75rem 1rem; 6 | line-height: 1; 7 | background: clr('bg2'); 8 | } 9 | 10 | input.form-control.-is-inline { 11 | padding: 0.4rem 1rem; 12 | color: clr('heading'); 13 | } 14 | -------------------------------------------------------------------------------- /docs/app/codes.ts: -------------------------------------------------------------------------------- 1 | const code: any = {}; 2 | 3 | code.NPM_INSTALL = `> npm install toppy --save //or 4 | > yarn add toppy 5 | `; 6 | 7 | code.INSTALL = `const position = new GlobalPosition(); 8 | const ref = this.toppy.overlay(position).host().create(); 9 | ref.open(); // use it anywhere`; 10 | 11 | export { code }; 12 | -------------------------------------------------------------------------------- /docs/styles/components/_hero.scss: -------------------------------------------------------------------------------- 1 | .comp.hero { 2 | background: #24283c; 3 | height: 100%; 4 | display: flex; 5 | align-items: center; 6 | flex-direction: column; 7 | justify-content: center; 8 | h1 { 9 | font-weight: 300; 10 | color: #fff; 11 | } 12 | p { 13 | color: #aaa; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/styles/components/_controls.scss: -------------------------------------------------------------------------------- 1 | .comp.controls { 2 | display: flex; 3 | flex-wrap: wrap; 4 | align-items: center; 5 | justify-content: start; 6 | line-height: 3; 7 | // background: #f4f4f9; 8 | padding: 1rem; 9 | li { 10 | display: flex; 11 | flex: 0 0 9rem; 12 | margin: 0.5rem; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/position/fullscreen-position.ts: -------------------------------------------------------------------------------- 1 | import { PositionMeta } from '../models'; 2 | import { ToppyPosition } from './position'; 3 | 4 | export class FullscreenPosition extends ToppyPosition { 5 | getPositions(): PositionMeta { 6 | return { top: 0, left: 0, width: '100%', height: '100%', position: 'fixed' }; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/app/host-components/hero-screen/hero-screen.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-hero-screen', 5 | templateUrl: './hero-screen.component.html', 6 | styles: [] 7 | }) 8 | export class HeroScreenComponent { 9 | close; 10 | dispose() { 11 | this.close(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docs/styles/components/_blade.scss: -------------------------------------------------------------------------------- 1 | .comp.blade { 2 | background: #3a3d40; 3 | height: 100%; 4 | color: #fff; 5 | h3 { 6 | font-weight: 300; 7 | text-align: center; 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | flex: 1; 12 | height: 100%; 13 | opacity: 0.9; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /projects/toppy/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /docs/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/toppy.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { ToppyComponent } from './toppy.component'; 4 | 5 | @NgModule({ 6 | imports: [CommonModule], 7 | declarations: [ToppyComponent], 8 | entryComponents: [ToppyComponent] 9 | }) 10 | export class ToppyModule {} 11 | -------------------------------------------------------------------------------- /docs/app/host-components/tooltip/tooltip.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-tooltip', 5 | templateUrl: './tooltip.component.html', 6 | styles: [] 7 | }) 8 | export class TooltipComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /projects/toppy/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { InsidePlacement, OutsidePlacement, SlidePlacement } from './lib/models'; 2 | export { FullscreenPosition, GlobalPosition, RelativePosition, SlidePosition } from './lib/position'; 3 | export { Toppy } from './lib/toppy'; 4 | export { ToppyControl } from './lib/toppy-control'; 5 | export { ToppyModule } from './lib/toppy.module'; 6 | -------------------------------------------------------------------------------- /docs/app/host-components/simple-list/simple-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-simple-list', 5 | templateUrl: './simple-list.component.html', 6 | styles: [] 7 | }) 8 | export class SimpleListComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /docs/app/host-components/simple-modal/simple-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-simple-modal', 5 | templateUrl: './simple-modal.component.html', 6 | styles: [] 7 | }) 8 | export class SimpleModalComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /docs/app/utils/section/section.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-section', 5 | templateUrl: './section.component.html' 6 | }) 7 | export class SectionComponent implements OnInit { 8 | @Input() 9 | heading = ''; 10 | @Input() 11 | icon = ''; 12 | constructor() {} 13 | 14 | ngOnInit() {} 15 | } 16 | -------------------------------------------------------------------------------- /docs/app/utils/sub-section/sub-section.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-sub-section', 5 | templateUrl: './sub-section.component.html', 6 | styles: [] 7 | }) 8 | export class SubSectionComponent implements OnInit { 9 | @Input() 10 | heading; 11 | constructor() {} 12 | 13 | ngOnInit() {} 14 | } 15 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to toppy-app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /docs/app/test/test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-test', 5 | templateUrl: './test.component.html', 6 | styleUrls: ['./test.component.scss'] 7 | }) 8 | export class TestComponent implements OnInit { 9 | close; 10 | constructor() {} 11 | 12 | ngOnInit() {} 13 | dismiss() { 14 | this.close(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/styles/helpers/mixins/_placeholder.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-no-vendor-prefix */ 2 | @mixin input-placeholder { 3 | &.placeholder { 4 | @content; 5 | } 6 | &:-moz-placeholder { 7 | @content; 8 | } 9 | &::-moz-placeholder { 10 | @content; 11 | } 12 | &:-ms-input-placeholder { 13 | @content; 14 | } 15 | &::-webkit-input-placeholder { 16 | @content; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/position/position.ts: -------------------------------------------------------------------------------- 1 | export abstract class ToppyPosition { 2 | protected config = {}; 3 | abstract getPositions(host: HTMLElement): any; 4 | 5 | getClassName(): string { 6 | return this.constructor.name.replace('Pos', '-pos').toLowerCase(); 7 | } 8 | 9 | updateConfig(config) { 10 | this.config = { ...this.config, ...config }; 11 | } 12 | 13 | init(tid: string) {} 14 | } 15 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/config.ts: -------------------------------------------------------------------------------- 1 | import { ToppyConfig } from './models'; 2 | 3 | export const DefaultConfig: ToppyConfig = { 4 | containerClass: 't-overlay', 5 | bodyClass: 't-open', 6 | wrapperClass: '', 7 | backdropClass: '', 8 | backdrop: false, 9 | closeOnDocClick: false, 10 | listenWindowEvents: true, 11 | closeOnEsc: false, 12 | windowResizeCallback: () => {}, 13 | docClickCallback: () => {} 14 | }; 15 | -------------------------------------------------------------------------------- /scripts/gh-pages.js: -------------------------------------------------------------------------------- 1 | const ghpages = require('gh-pages'); 2 | ghpages.publish( 3 | './dist/toppy-app', 4 | { 5 | repo: 'https://' + process.env.GH_TOKEN + '@github.com/lokesh-coder/toppy.git', 6 | add: true 7 | }, 8 | function(err) { 9 | if (err) { 10 | console.log('Error occured during gh pages push', err); 11 | } else { 12 | console.log('Pushed to ghpages!'); 13 | } 14 | } 15 | ); 16 | -------------------------------------------------------------------------------- /docs/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | 14 | -------------------------------------------------------------------------------- /docs/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /docs/styles/animations/_slide_in.scss: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------- 2 | .slide-in-left { 3 | animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; 4 | } 5 | ---------------------------------------------- */ 6 | 7 | @keyframes slide-in-left { 8 | 0% { 9 | opacity: 0; 10 | transform: translateX(-10px); 11 | } 12 | 13 | 100% { 14 | opacity: 1; 15 | transform: translateX(0); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/app/test/test.component.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 3 | 'Segoe UI Emoji', 'Segoe UI Symbol'; 4 | background: #e91e63; 5 | padding: 5px; 6 | border-radius: 3px; 7 | font-size: 14px; 8 | margin-top: 0.2rem; 9 | color: #fff; 10 | } 11 | 12 | p { 13 | margin: 0; 14 | font-weight: 500; 15 | margin-bottom: 10px; 16 | } 17 | -------------------------------------------------------------------------------- /docs/styles/browsers/_chrome.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable media-feature-name-no-vendor-prefix */ 2 | @media all and (-webkit-min-device-pixel-ratio: 1) { 3 | } 4 | 5 | 6 | /* Change the white to any color ;) */ 7 | 8 | @-webkit-keyframes autofill { 9 | to { 10 | color: #666; 11 | background: transparent; 12 | } 13 | } 14 | 15 | input:-webkit-autofill { 16 | -webkit-animation-name: autofill; 17 | -webkit-animation-fill-mode: both; 18 | } -------------------------------------------------------------------------------- /docs/styles/root.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable scss/at-import-no-partial-leading-underscore */ 2 | 3 | @import './config'; 4 | @import './helpers/_index'; 5 | @import './base/_index'; 6 | @import './vendors/_index'; 7 | @import './pages/_index'; 8 | @import './animations/_index'; 9 | @import './components/_index'; 10 | @import './layouts/_index'; 11 | @import './elements/_index'; 12 | @import './browsers/_index'; 13 | @import './responsive/_index'; 14 | @import './temp'; 15 | -------------------------------------------------------------------------------- /docs/styles/helpers/mixins/_smooth-font.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable */ 2 | 3 | @mixin smooth-font($lines: 1, $substract: 0) { 4 | font-size: 100%; 5 | -webkit-text-size-adjust: 100%; 6 | font-variant-ligatures: none; 7 | -webkit-font-variant-ligatures: none; 8 | text-rendering: optimizeLegibility; 9 | -moz-osx-font-smoothing: grayscale; 10 | font-smoothing: antialiased; 11 | -webkit-font-smoothing: antialiased; 12 | text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px; 13 | } 14 | -------------------------------------------------------------------------------- /docs/app/host-components/hero-screen/hero-screen.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Hello, Its fullscreen overlay

4 |

5 | It occupies complete screen width and height. Please click ESCAPE key or click close button to close the overlay 6 |

7 | 10 |
11 |
12 | -------------------------------------------------------------------------------- /docs/styles/vendors/_pretty-checkbox.scss: -------------------------------------------------------------------------------- 1 | $pretty--color-success: clr('success'); 2 | @import '~pretty-checkbox/src/pretty-checkbox'; 3 | 4 | .pretty.pretty-check { 5 | .state label:before, 6 | .pretty.p-round .state label:after { 7 | border-width: 0.15rem; 8 | } 9 | .icon { 10 | transform: scale(0.8); 11 | } 12 | } 13 | 14 | .pretty.p-switch .state label:before, 15 | .pretty.p-switch .state label:after { 16 | top: calc((0% - (100% - 1em)) - 12%); 17 | } 18 | -------------------------------------------------------------------------------- /docs/styles/components/_modal.scss: -------------------------------------------------------------------------------- 1 | .modal-container { 2 | background: #fff; 3 | border-radius: 4px; 4 | box-shadow: 4px 5px 19px #62616b; 5 | overflow: hidden; 6 | color: clr('dark'); 7 | } 8 | 9 | .modal-header { 10 | padding: 20px; 11 | font-weight: 400; 12 | background: #f0f0ff; 13 | text-align: center; 14 | font-size: 1.4rem; 15 | } 16 | .modal-body { 17 | padding: 20px; 18 | .illus { 19 | min-height: 192px; 20 | } 21 | } 22 | .modal-footer { 23 | text-align: right; 24 | padding: 20px; 25 | } 26 | -------------------------------------------------------------------------------- /projects/toppy/src/tests/positions/fullscreen-position.spec.ts: -------------------------------------------------------------------------------- 1 | import { FullscreenPosition } from '../../lib/position'; 2 | 3 | describe('@ FullscreenPosition', () => { 4 | describe('#getPositions', () => { 5 | it('should return proper position', () => { 6 | const slidePos = new FullscreenPosition(); 7 | expect(slidePos.getPositions()).toEqual({ 8 | top: 0, 9 | left: 0, 10 | width: '100%', 11 | height: '100%', 12 | position: 'fixed' 13 | }); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /docs/app/examples/control-example/control-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Toppy } from '../../../../projects/toppy/src/lib/toppy'; 3 | 4 | @Component({ 5 | selector: 'app-control-example', 6 | templateUrl: './control-example.component.html', 7 | styleUrls: ['./control-example.component.scss'] 8 | }) 9 | export class ControlExampleComponent implements OnInit { 10 | constructor(private toppy: Toppy) {} 11 | 12 | ngOnInit() {} 13 | open() { 14 | this.toppy.getCtrl('sonia').open(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": ["node_modules/@types"], 14 | "lib": ["es2018", "dom"], 15 | "paths": { 16 | "toppy": ["projects/toppy/src/public_api.ts"], 17 | "toppy/*": ["projects/toppy/src/*"] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/styles/helpers/mixins/_ellipsis.scss: -------------------------------------------------------------------------------- 1 | /// div { 2 | /// @include ellipsis; 3 | /// } 4 | /// 5 | /// .box { 6 | /// @include ellipsis(3); 7 | /// } 8 | 9 | /* stylelint-disable */ 10 | 11 | @mixin ellipsis($lines: 1, $substract: 0) { 12 | @if $lines == 1 { 13 | overflow: hidden; 14 | text-overflow: ellipsis; 15 | white-space: nowrap; 16 | width: 100% - $substract; 17 | } @else { 18 | -webkit-box-orient: vertical; 19 | box-orient: vertical; 20 | display: -webkit-box; 21 | display: box; 22 | -webkit-line-clamp: $lines; 23 | line-clamp: $lines; 24 | overflow: hidden; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/styles/base/_utility.scss: -------------------------------------------------------------------------------- 1 | .doc-util-flex-2 { 2 | flex: 2 !important; 3 | } 4 | 5 | .doc-util-valign-middle { 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: center; 9 | } 10 | 11 | .doc-util-tt-lowercase { 12 | text-transform: lowercase; 13 | } 14 | 15 | .doc-util-cover-span { 16 | display: flex; 17 | flex-direction: column; 18 | } 19 | 20 | .doc-util-cursor-pointer { 21 | cursor: pointer; 22 | } 23 | 24 | .doc-util-pos-right { 25 | right: 0; 26 | left: auto !important; 27 | } 28 | 29 | .ff-asap { 30 | } 31 | 32 | .--only-on-parent-hover { 33 | display: none !important; 34 | } 35 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/styles.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | left: 0; 3 | position: fixed; 4 | top: 0; 5 | width: 100%; 6 | height: 100%; 7 | pointer-events: none; 8 | &.no-pointers { 9 | pointer-events: all; 10 | } 11 | .t-backdrop { 12 | left: 0; 13 | position: fixed; 14 | top: 0; 15 | width: 100%; 16 | height: 100%; 17 | background: rgba(0, 0, 0, 0.5); 18 | // pointer-events: none; 19 | } 20 | > div.t-wrapper { 21 | position: absolute; 22 | visibility: hidden; 23 | opacity: 0; 24 | transition: opacity 0.2s ease; 25 | overflow: hidden; 26 | pointer-events: all; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/styles/base/_reset.scss: -------------------------------------------------------------------------------- 1 | html { 2 | @include smooth-font(); 3 | font-size: $toppy--base-font-size; 4 | } 5 | 6 | .custom-select { 7 | -moz-appearance: none; 8 | -webkit-appearance: none; 9 | } 10 | 11 | .btn.btn-link { 12 | color: clr('link-alt'); 13 | font-weight: 500; 14 | &:hover { 15 | text-decoration: none; 16 | } 17 | } 18 | 19 | .btn.btn-lg { 20 | font-size: 1rem; 21 | } 22 | 23 | button.btn, 24 | a.btn { 25 | line-height: 1; 26 | padding: 0.5rem 1rem; 27 | } 28 | 29 | .btn.btn-success, 30 | .btn.btn-danger, 31 | .btn.btn-primary { 32 | color: #fff; 33 | } 34 | a:not([href]):not([tabindex]) { 35 | color: none !important; 36 | } 37 | -------------------------------------------------------------------------------- /docs/app/examples/drag-example/drag-example.component.html: -------------------------------------------------------------------------------- 1 | 8 |
9 | 17 |
18 | 19 |
20 | Reset 21 |
22 | -------------------------------------------------------------------------------- /docs/assets/archived-versions.json: -------------------------------------------------------------------------------- 1 | {"undefined":1541115139547,"1.0.11":1541115874348,"1.0.12":1541117479211,"1.0.13":1541118084059,"1.0.14":1541236752420,"1.0.15":1541524275096,"1.0.16":1541786515110,"1.0.17":1542127897095,"1.0.18":1542339601683,"1.1.0":1542548397540,"1.1.1":1542860347098,"1.2.0":1543048207817,"1.2.2":1543389399932,"1.2.3":1543405671424,"1.2.4":1543409608479,"1.3.0":1543944610263,"1.3.1":1543996147918,"2.0.0":1544793410445,"2.0.1":1544794243418,"2.0.2":1544841561628,"2.0.3":1544845267568,"2.0.4":1545292006287,"2.0.5":1545654967450,"2.1.0":1545763951127,"2.2.0":1546233019376,"2.3.0":1546714578991,"2.3.1":1549813301631,"2.3.2":1549952769013,"2.3.3":1552500395017,"2.3.4":1564588293511} 2 | -------------------------------------------------------------------------------- /docs/styles/components/_target-element-container.scss: -------------------------------------------------------------------------------- 1 | .comp.target-element-container { 2 | padding: 2rem; 3 | display: flex; 4 | align-items: center; 5 | justify-content: center; 6 | .target-element { 7 | background: clr('primary'); 8 | padding: 1rem; 9 | border-radius: 2px; 10 | color: #fff; 11 | font-weight: 500; 12 | box-shadow: 0px 3px 6px #c6c6d8; 13 | cursor: pointer; 14 | } 15 | 16 | .target-element-basic { 17 | padding: 1rem 2rem; 18 | border: 1px dashed #d8d8e3; 19 | cursor: pointer; 20 | } 21 | 22 | .target-element-basic:hover { 23 | border-color: #cfd2de; 24 | color: clr('dark'); 25 | background-color: #fff; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /scripts/archive.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const version = process.argv[2]; 3 | const archiveMetaFile = './docs/assets/archived-versions.json'; 4 | 5 | async function archiveFiles() { 6 | try { 7 | await fs.copy('./dist/toppy-app', `./dist/${version}`); 8 | await fs.copy(`./dist/${version}`, `./dist/toppy-app/${version}`); 9 | let archivedData = await fs.readJson(archiveMetaFile); 10 | archivedData = archivedData || {}; 11 | archivedData[version] = Date.now(); 12 | await fs.writeJson(archiveMetaFile, archivedData); 13 | console.log(`successfully archived version ${version}!`); 14 | } catch (err) { 15 | console.error(err); 16 | } 17 | } 18 | 19 | archiveFiles(); 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | .history/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /documentation 33 | /libpeerconnection.log 34 | npm-debug.log 35 | yarn-error.log 36 | testem.log 37 | /typings 38 | 39 | # System Files 40 | .DS_Store 41 | Thumbs.db 42 | -------------------------------------------------------------------------------- /docs/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /docs/app/test/test.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TestComponent } from './test.component'; 4 | 5 | describe('TestComponent', () => { 6 | let component: TestComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TestComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TestComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /docs/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /docs/styles/vendors/_prism.scss: -------------------------------------------------------------------------------- 1 | app-content pre[class*='language-'] { 2 | background: #fff; 3 | border: 1px solid clr('border'); 4 | border-radius: $toppy--border-rad; 5 | code { 6 | font-size: 12px; 7 | font-family: $toppy--font-family-monospace; 8 | } 9 | } 10 | app-content .inline-code { 11 | display: flex; 12 | align-items: center; 13 | flex-wrap: wrap; 14 | pre[class*='language-'] { 15 | font-size: 13px !important; 16 | background-color: #f0f0f3; 17 | border: 0; 18 | margin: 6px; 19 | padding: 2px 8px; 20 | code{ 21 | 22 | font-size: 10px !important; 23 | } 24 | } 25 | } 26 | 27 | app-content code[class*='language-'], 28 | app-content pre[class*='language-'] { 29 | color: #51585d; 30 | } 31 | -------------------------------------------------------------------------------- /docs/app/examples/ribbon-example/ribbon-example.component.html: -------------------------------------------------------------------------------- 1 |
Open ribbon
2 | 3 | 4 |
5 |
6 |
7 |

8 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur natus, esse amet nisi laboriosam omnis 9 | tenetur accusantium vero at totam ducimus itaque soluta adipisci mollitia expedita dolores quaerat ratione 10 | iure? 11 |

12 | 13 |
14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/template.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
{{ content.data }}
6 |
7 |
8 | 9 | 10 | 11 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /projects/toppy/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | import 'zone.js/dist/zone-testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | declare const require: any; 13 | 14 | // First, initialize the Angular testing environment. 15 | getTestBed().initTestEnvironment( 16 | BrowserDynamicTestingModule, 17 | platformBrowserDynamicTesting() 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /projects/toppy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toppy", 3 | "version": "0.0.0-development", 4 | "description": "Overlay library for Angular 7+", 5 | "keywords": [ 6 | "angular", 7 | "dropdown", 8 | "modal", 9 | "overlay", 10 | "popover", 11 | "popup", 12 | "sidebar", 13 | "tooltip" 14 | ], 15 | "homepage": "https://github.com/lokesh-coder/toppy", 16 | "bugs": { 17 | "url": "https://github.com/lokesh-coder/toppy/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/lokesh-coder/toppy" 22 | }, 23 | "license": "MIT", 24 | "author": { 25 | "name": "Lokesh Rajendran", 26 | "email": "mexican.dirtyfellow@gmail.com", 27 | "url": "https://github.com/lokesh-coder" 28 | }, 29 | "peerDependencies": { 30 | "@angular/common": "^7.0.0", 31 | "@angular/core": "^7.0.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /projects/toppy/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2018" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "annotateForClosureCompiler": true, 22 | "skipTemplateCodegen": true, 23 | "strictMetadataEmit": true, 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true, 26 | "enableResourceInlining": true 27 | }, 28 | "exclude": [ 29 | "src/test.ts", 30 | "**/*.spec.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/position/slide-position.ts: -------------------------------------------------------------------------------- 1 | import { PositionMeta, SlidePlacement } from '../models'; 2 | import { ToppyPosition } from './position'; 3 | 4 | interface SlidePlacementConfig { 5 | placement?: SlidePlacement; 6 | width?: string; 7 | } 8 | 9 | export class SlidePosition extends ToppyPosition { 10 | protected config: SlidePlacementConfig = { placement: SlidePlacement.LEFT, width: '30%' }; 11 | 12 | constructor(config: SlidePlacementConfig) { 13 | super(); 14 | this.config = { ...this.config, ...config }; 15 | } 16 | getPositions(): PositionMeta { 17 | const props = this.config.placement === SlidePlacement.LEFT ? { left: 0 } : { right: 0 }; 18 | return { 19 | ...props, 20 | top: 0, 21 | width: this.config.width, 22 | height: '100%', 23 | position: 'fixed', 24 | extra: this.config.placement 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/styles/helpers/mixins/_gradient.scss: -------------------------------------------------------------------------------- 1 | @function is-direction($value) { 2 | $is-keyword: index( 3 | 4 | ( 5 | to top, 6 | to top right, 7 | to right top, 8 | to right, 9 | to bottom right, 10 | to right bottom, 11 | to bottom, 12 | to bottom left, 13 | to left bottom, 14 | to left, 15 | to left top, 16 | to top left 17 | ), 18 | $value 19 | ); 20 | $is-angle: type-of($value)=='number' and index('deg' 'grad' 'turn' 'rad', unit($value)); 21 | @return $is-keyword or $is-angle; 22 | } 23 | @mixin linear-gradient($direction, $color-stops...) { 24 | @if is-direction($direction) ==false { 25 | $color-stops: $direction, $color-stops; 26 | $direction: 180deg; 27 | } 28 | background: nth(nth($color-stops, 1), 1); 29 | background: -webkit-linear-gradient(legacy-direction($direction), $color-stops); 30 | background: linear-gradient($direction, $color-stops); 31 | } 32 | -------------------------------------------------------------------------------- /docs/app/examples/modal-example/modal-example.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |
8 | 9 | 10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/app/examples/slide-position-example/slide-position-example.component.html: -------------------------------------------------------------------------------- 1 |
2 |
Open sidebar
3 |
4 |
5 | 24 |
25 | -------------------------------------------------------------------------------- /docs/app/examples/global-position-example/global-position-example.component.html: -------------------------------------------------------------------------------- 1 |
2 |
click me
3 |
4 |
5 | 24 |
25 | -------------------------------------------------------------------------------- /docs/app/examples/dropdown-example/dropdown-example.component.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | I like {{ selectedEatable.name }} 7 |
8 | 9 | 10 | 15 | 16 | 17 | 18 | 27 | 28 | -------------------------------------------------------------------------------- /docs/app/examples/fullscreen-position-example/fullscreen-position-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FullscreenPosition, Toppy, ToppyControl } from 'toppy'; 3 | import { HeroScreenComponent } from '../../host-components/hero-screen/hero-screen.component'; 4 | 5 | @Component({ 6 | selector: 'app-fullscreen-position-example', 7 | templateUrl: './fullscreen-position-example.component.html' 8 | }) 9 | export class FullscreenPositionExampleComponent implements OnInit { 10 | private _toppyControl: ToppyControl; 11 | constructor(private toppy: Toppy) {} 12 | 13 | ngOnInit() { 14 | this._toppyControl = this.toppy 15 | .position(new FullscreenPosition()) 16 | .config({ 17 | closeOnEsc: true 18 | }) 19 | .content(HeroScreenComponent) 20 | .create(); 21 | this._toppyControl.listen('t_compins').subscribe(d => { 22 | console.log('HeroScreenComponent initiated', d); 23 | }); 24 | } 25 | 26 | open() { 27 | this._toppyControl.open(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /docs/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from "@angular/core/testing"; 2 | import { AppComponent } from "./app.component"; 3 | describe("AppComponent", () => { 4 | beforeEach(async(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [AppComponent] 7 | }).compileComponents(); 8 | })); 9 | it("should create the app", async(() => { 10 | const fixture = TestBed.createComponent(AppComponent); 11 | const app = fixture.debugElement.componentInstance; 12 | expect(app).toBeTruthy(); 13 | })); 14 | it(`should have as title 'toppy app'`, async(() => { 15 | const fixture = TestBed.createComponent(AppComponent); 16 | const app = fixture.debugElement.componentInstance; 17 | expect(app.title).toEqual("toppy-app"); 18 | })); 19 | it("should render title in a h1 tag", async(() => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | fixture.detectChanges(); 22 | const compiled = fixture.debugElement.nativeElement; 23 | expect(compiled.querySelector("h1").textContent).toContain("Welcome to toppy App!"); 24 | })); 25 | }); 26 | -------------------------------------------------------------------------------- /docs/app/examples/relative-position-example/relative-position-example.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | I am a target element
4 | Hover me 5 |
6 |
7 |
8 | 29 |
30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Lokesh Rajendran ( mexican.dirtyfellow@gmail.com ) 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /projects/toppy/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular', 'viewport'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-viewport'), 11 | require('karma-chrome-launcher'), 12 | require('karma-jasmine-html-reporter'), 13 | require('karma-coverage-istanbul-reporter'), 14 | require('@angular-devkit/build-angular/plugins/karma') 15 | ], 16 | client: { 17 | clearContext: false // leave Jasmine Spec Runner output visible in browser 18 | }, 19 | coverageIstanbulReporter: { 20 | dir: require('path').join(__dirname, '../../coverage'), 21 | reports: ['html', 'lcovonly', 'json'], 22 | fixWebpackSourcePaths: true 23 | }, 24 | reporters: ['progress', 'kjhtml'], 25 | port: 9876, 26 | colors: true, 27 | logLevel: config.LOG_INFO, 28 | autoWatch: true, 29 | browsers: ['Chrome'], 30 | singleRun: false 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /docs/app/examples/ribbon-example/ribbon-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; 2 | import { InsidePlacement } from '../../../../projects/toppy/src/lib/models'; 3 | import { GlobalPosition, Toppy, ToppyControl } from '../../../../projects/toppy/src/public_api'; 4 | 5 | @Component({ 6 | selector: 'app-ribbon-example', 7 | templateUrl: './ribbon-example.component.html', 8 | styleUrls: ['./ribbon-example.component.scss'] 9 | }) 10 | export class RibbonExampleComponent implements OnInit { 11 | private _toppyControl: ToppyControl; 12 | @ViewChild('tpl', { read: TemplateRef }) tpl: TemplateRef; 13 | constructor(private toppy: Toppy) {} 14 | 15 | ngOnInit() { 16 | this._toppyControl = this.toppy 17 | .position( 18 | new GlobalPosition({ 19 | placement: InsidePlacement.BOTTOM, 20 | width: '100%', 21 | height: 'auto' 22 | }) 23 | ) 24 | .config({ 25 | closeOnDocClick: false 26 | }) 27 | .content(this.tpl) 28 | .create(); 29 | } 30 | open() { 31 | this._toppyControl.open(); 32 | } 33 | close() { 34 | this._toppyControl.close(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/app/utils/scollspy.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, EventEmitter, HostListener, Input, Output } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[scrollSpy]' 5 | }) 6 | export class ScrollSpyDirective { 7 | @Input() public spiedTags = []; 8 | @Output() public sectionChange = new EventEmitter(); 9 | private currentSection: string; 10 | 11 | constructor(private _el: ElementRef) {} 12 | 13 | @HostListener('window:scroll', ['$event']) 14 | onScroll(event: any) { 15 | let currentSection: string; 16 | const children = document.querySelectorAll('h3'); 17 | const scrollTop = document.documentElement.scrollTop; 18 | const parentOffset = document.documentElement.offsetTop; 19 | for (let i = 0; i < children.length; i++) { 20 | const element = children[i]; 21 | if (this.spiedTags.some(spiedTag => spiedTag === element.tagName)) { 22 | if (element.offsetTop - parentOffset <= scrollTop) { 23 | currentSection = element.id; 24 | } 25 | } 26 | } 27 | if (currentSection !== this.currentSection) { 28 | this.currentSection = currentSection; 29 | this.sectionChange.emit(this.currentSection); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /projects/toppy/src/tests/positions/slide-position.spec.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { SlidePlacement } from '../../lib/models'; 4 | import { SlidePosition } from '../../lib/position'; 5 | 6 | describe('@ SlidePosition', () => { 7 | describe('#getPositions', () => { 8 | it('should return proper position when placement is left', () => { 9 | const slidePos = new SlidePosition({ 10 | width: '200px', 11 | placement: SlidePlacement.LEFT 12 | }); 13 | expect(slidePos.getPositions()).toEqual({ 14 | left: 0, 15 | top: 0, 16 | width: '200px', 17 | height: '100%', 18 | position: 'fixed', 19 | extra: SlidePlacement.LEFT 20 | }); 21 | }); 22 | it('should return proper position when placement is right', () => { 23 | const slidePos = new SlidePosition({ 24 | width: '500', 25 | placement: SlidePlacement.RIGHT 26 | }); 27 | expect(slidePos.getPositions()).toEqual({ 28 | right: 0, 29 | top: 0, 30 | width: '500', 31 | height: '100%', 32 | position: 'fixed', 33 | extra: SlidePlacement.RIGHT 34 | }); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /docs/styles/_config.scss: -------------------------------------------------------------------------------- 1 | $toppy--base-font-size: 13px; 2 | $toppy--base-font-size-xl: 16px; 3 | $toppy--app-name: 'TOPPY'; 4 | $toppy--debug: false; 5 | $toppy--font-family: 'Rubik', acumin-pro, 'Inconsolata', 'Inter UI', 'Nunito Sans'; 6 | $toppy--font-family-headings: 'acumin-pro'; 7 | $toppy--font-family-monospace: 'Roboto mono', 'calling-code', 'Courier New', 'Lucida Console', Monaco, Courier, 8 | monospace; 9 | 10 | $toppy--colors: ( 11 | 'bg': #fafafa, 12 | 'text': #88889a, 13 | 'heading': #9590d4, 14 | 'border': #e9edff, 15 | 'dark': #585c6d, 16 | 'shadow': #dddde9, 17 | 'primary': #3F51B5, 18 | 'secondary': #ef8fb7, 19 | 'success': #3fc782, 20 | 'warning': #f78f1e, 21 | 'danger': #e3505a, 22 | 'info': #03a9f4, 23 | 'yellow': #cabcb1, 24 | 'alt': #3F51B5 25 | ); 26 | 27 | $toppy--font-sizes: ( 28 | 'sm': 0.9rem, 29 | 'md': 1rem, 30 | 'lg': 1.4rem, 31 | 'xl': 4rem, 32 | 'h1': 3.2rem, 33 | 'h2': 2rem, 34 | 'h3': 1.75rem, 35 | 'h4': 1.5rem, 36 | 'h5': 1.25rem, 37 | 'h6': 1rem 38 | ); 39 | 40 | $toppy--border-rad: 3px; 41 | $toppy--icons-font: 'toppy'; 42 | $toppy--icon-font-path: './icons'; 43 | 44 | $toppy--grid-breakpoints: ( 45 | xs: 0, 46 | sm: 576px, 47 | md: 768px, 48 | lg: 992px, 49 | xl: 1600px 50 | ); 51 | -------------------------------------------------------------------------------- /docs/app/examples/slide-position-example/slide-position-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { SlidePlacement, SlidePosition, Toppy, ToppyControl } from 'toppy'; 3 | import { SimpleListComponent } from '../../host-components/simple-list/simple-list.component'; 4 | 5 | @Component({ 6 | selector: 'app-slide-position-example', 7 | templateUrl: './slide-position-example.component.html', 8 | styles: [] 9 | }) 10 | export class SlidePositionExampleComponent implements OnInit { 11 | placements: { name: string; value: SlidePlacement }[] = [ 12 | { name: 'Left', value: SlidePlacement.LEFT }, 13 | { name: 'Right', value: SlidePlacement.RIGHT } 14 | ]; 15 | selectedPlacement = this.placements[0].value; 16 | private _toppyControl: ToppyControl; 17 | constructor(private toppy: Toppy) {} 18 | 19 | ngOnInit() {} 20 | 21 | open() { 22 | if (this._toppyControl) { 23 | this._toppyControl.close(); 24 | } 25 | this._toppyControl = this.toppy 26 | .position(new SlidePosition({ placement: this.selectedPlacement })) 27 | .config({ 28 | closeOnDocClick: true 29 | }) 30 | .content(SimpleListComponent) 31 | .create(); 32 | this._toppyControl.open(); 33 | } 34 | 35 | onOptionChange() { 36 | console.log('option changed'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/assets/favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 17 | 21 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - '~/.npm' 5 | - node_modules 6 | notifications: 7 | email: false 8 | slack: 9 | secure: E3zMq5rOujXNqci/8aKxtDDFYNb3QqEi8O9n2enTpLzuK5qVPnaVDzXre7MxLN13LYsRMzT3hpzngOu09Zj1Gt49MCXBDjseF6tVddEE2hURVCVSF87Of4pMUbxxC8zmIwMk33UBGGkS2F5fvkKiX1KL2LdqoJ+2GwD6xq0swPXC+LvbbT0bMif0jFzyJKYyqvT13UzMg+6m4obrfKbaXUdrG7woFs1UMhwkSY4i167w/l3+hYGY4TsOENhoqdUahP3CGu1v6zesLacwGk8A8H00A8dS5oXu0XuWZ+AdFOBpNBHj0JOWkFmMdDTtwOzKIYrGnPHbE1U0/ROdqguT6EBVqnOIHnGsjvBkqUN9mVrR+9iLcdODnsv9MkDEKnxrcpUcCITfTdsaCktIC17Xoe14WsIAB+doxq9fYGvBqqTj7GzMA+8a36ae2AaQDNrAJ+YojbqkisWc/BkkFFHd2EISges/nYmDqDcdk0UpfNuAeheynEiIFKISoxGjd5w/VI9oopY250u2qAwEPdvV8tn31WOxLlx00oe928bY4yPzMv/CkRInXzhmddCZidBGKh7nKTDut4VST/nwawkey5G1xYzHOzxSX1z7X8RQkMocWZ5etGv9bCxiOV/RSkhjzu8eLHKqUd5xs/+PYDxmG3iFzmVXEf4mELm7Bw2JuxE= 10 | node_js: 11 | - 9.3.0 12 | install: 13 | - npm install 14 | - npm install -g codecov 15 | before_script: 16 | - export CHROME_BIN=/usr/bin/google-chrome 17 | - export DISPLAY=:99.0 18 | - sh -e /etc/init.d/xvfb start 19 | - sudo apt-get update 20 | - sudo apt-get install -y libappindicator1 fonts-liberation 21 | - wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 22 | - sudo dpkg -i google-chrome*.deb 23 | script: 24 | - npm run test:lib -- --watch=false 25 | - codecov 26 | - npm run build:lib 27 | - npm run build:app 28 | after_success: 29 | - npm run travis-deploy-once "npm run release" 30 | branches: 31 | except: 32 | - "/^v\\d+\\.\\d+\\.\\d+$/" 33 | -------------------------------------------------------------------------------- /docs/app/examples/modal-example/modal-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; 2 | import { GlobalPosition, InsidePlacement, Toppy, ToppyControl } from 'toppy'; 3 | 4 | @Component({ 5 | selector: 'app-modal-example', 6 | templateUrl: './modal-example.component.html', 7 | styleUrls: ['./modal-example.component.scss'] 8 | }) 9 | export class ModalExampleComponent implements OnInit { 10 | _toppyControl: ToppyControl; 11 | _toppyControl2: ToppyControl; 12 | @ViewChild('modalTpl', { read: TemplateRef }) modalTpl: TemplateRef; 13 | 14 | constructor(private toppy: Toppy) {} 15 | 16 | ngOnInit() { 17 | this._toppyControl = this.toppy 18 | .position( 19 | new GlobalPosition({ 20 | placement: InsidePlacement.CENTER, 21 | width: 300, 22 | height: 'auto' 23 | }) 24 | ) 25 | .config({ 26 | backdrop: true, 27 | closeOnDocClick: false, 28 | closeOnEsc: true 29 | }) 30 | .content(this.modalTpl) 31 | .create(); 32 | 33 | this._toppyControl2 = this.toppy 34 | .position( 35 | new GlobalPosition({ 36 | placement: InsidePlacement.CENTER, 37 | width: 'auto', 38 | height: 'auto' 39 | }) 40 | ) 41 | .config({ 42 | closeOnEsc: true, 43 | closeOnDocClick: true 44 | }) 45 | .content('', { hasHTML: true }) 46 | .create(); 47 | } 48 | open() { 49 | this._toppyControl.open(); 50 | } 51 | openImage() { 52 | this._toppyControl2.open(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /docs/styles/base/_icons.scss: -------------------------------------------------------------------------------- 1 | $icons: ( 2 | alert-circle: '\e902', 3 | alert-triangle: '\e904', 4 | arrow-left: '\e910', 5 | arrow-right: '\e912', 6 | check-circle: '\e92c', 7 | copy: '\e944', 8 | edit: '\e958', 9 | external-link: '\e95b', 10 | file: '\e964', 11 | filter: '\e966', 12 | github: '\e970', 13 | hash: '\e975', 14 | heart: '\e977', 15 | help: '\e978', 16 | home: '\e979', 17 | link: '\e982', 18 | loader: '\e986', 19 | maximize-2: '\e98e', 20 | menu: '\e98f', 21 | minimize-2: '\e995', 22 | more-horizontal: '\e99b', 23 | navigation: '\e9a0', 24 | plus-circle: '\e9b2', 25 | radio: '\e9b7', 26 | repeat: '\e9ba', 27 | rss: '\e9be', 28 | settings: '\e9c4', 29 | share-2: '\e9c6', 30 | sliders: '\e9d1', 31 | tag: '\e9db', 32 | terminal: '\e9dd', 33 | twitter: '\e9ea', 34 | users: '\e9f6', 35 | x: '\ea02', 36 | x-circle: '\ea03', 37 | zap: '\ea06' 38 | ); 39 | 40 | @font-face { 41 | font-family: 'toppy'; 42 | font-style: normal; 43 | font-weight: normal; 44 | src: url('#{$toppy--icon-font-path}/toppy.ttf?olqd7c') format('truetype'), 45 | url('#{$toppy--icon-font-path}/toppy.woff?olqd7c') format('woff'), 46 | url('#{$toppy--icon-font-path}/toppy.svg?olqd7c#choosy') format('svg'); 47 | } 48 | 49 | .toppy-icon { 50 | /* use !important to prevent issues with browser extensions that change fonts */ 51 | font-family: $toppy--icons-font !important; 52 | 53 | /* Better Font Rendering =========== */ 54 | -webkit-font-smoothing: antialiased; 55 | font-style: normal; 56 | font-variant: normal; 57 | font-weight: normal; 58 | line-height: 1; 59 | -moz-osx-font-smoothing: grayscale; 60 | speak: none; 61 | text-transform: none; 62 | font-size: 80%; 63 | } 64 | @each $icon in $icons { 65 | .icon-#{nth($icon, 1)} { 66 | &:before { 67 | content: nth($icon, 2); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /docs/app/examples/dynamic-text-example/dynamic-text-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; 2 | import * as format from 'date-fns/format'; 3 | import { never, Observable, Subject, timer } from 'rxjs'; 4 | import { map, switchMap, timeInterval } from 'rxjs/operators'; 5 | import { OutsidePlacement } from '../../../../projects/toppy/src/lib/models'; 6 | import { RelativePosition, Toppy, ToppyControl } from '../../../../projects/toppy/src/public_api'; 7 | 8 | @Component({ 9 | selector: 'app-dynamic-text-example', 10 | templateUrl: './dynamic-text-example.component.html' 11 | }) 12 | export class DynamicTextExampleComponent implements OnInit { 13 | @ViewChild('el') el: ElementRef; 14 | private _toppyControl: ToppyControl; 15 | pauser = new Subject(); 16 | constructor(private toppy: Toppy) { 17 | this.pauser.pipe(switchMap(paused => (paused ? never() : this.source()))).subscribe(x => console.log(x)); 18 | } 19 | 20 | source(): Observable { 21 | return new Observable(observer => { 22 | timer(0, 100) 23 | .pipe( 24 | timeInterval(), 25 | map(() => this._toppyControl.updateTextContent.next(this.formatedTime())) 26 | ) 27 | .subscribe(); 28 | }); 29 | } 30 | 31 | formatedTime() { 32 | return format(new Date(), 'HH:mm:ss A'); 33 | } 34 | 35 | ngOnInit() { 36 | this._toppyControl = this.toppy 37 | .position( 38 | new RelativePosition({ 39 | placement: OutsidePlacement.RIGHT, 40 | src: this.el.nativeElement, 41 | width: 'auto', 42 | autoUpdate: true 43 | }) 44 | ) 45 | .content(this.formatedTime(), { class: 'tooltip' }) 46 | .create(); 47 | } 48 | 49 | onMouseOver() { 50 | this._toppyControl.open(); 51 | this.pauser.next(false); 52 | } 53 | onMouseLeave() { 54 | this._toppyControl.close(); 55 | this.pauser.next(true); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branch": "master", 3 | "plugins": [ 4 | [ 5 | "@semantic-release/exec", 6 | { 7 | "prepareCmd": "npm run archive:app -- ${nextRelease.version} && chmod a+x ./scripts/version.sh && ./scripts/version.sh ${nextRelease.version} && cp -r ./README.md ./dist/toppy && npx json -I -f package.json -e 'this.version=\"${nextRelease.version}\"'", 8 | "successCmd": "npm run postpublish -- ${nextRelease.version}" 9 | } 10 | ], 11 | [ 12 | "@semantic-release/commit-analyzer", 13 | { 14 | "releaseRules": [ 15 | { "type": "feat", "release": "minor" }, 16 | { "type": "fix", "release": "patch" }, 17 | { "type": "perf", "release": "patch" }, 18 | { "breaking": true, "release": "major" }, 19 | { "revert": true, "release": "patch" }, 20 | { "type": "docs", "release": "patch" }, 21 | { "type": "refactor", "release": "patch" }, 22 | { "type": "style", "release": "patch" } 23 | ], 24 | "parserOpts": { 25 | "noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"] 26 | } 27 | } 28 | ], 29 | "@semantic-release/changelog", 30 | "@semantic-release/release-notes-generator", 31 | [ 32 | "@semantic-release/npm", 33 | { 34 | "npmPublish": true, 35 | "tarballDir": "dist/toppy", 36 | "pkgRoot": "dist/toppy" 37 | } 38 | ], 39 | "@semantic-release/github", 40 | [ 41 | "@semantic-release/git", 42 | { 43 | "assets": [ 44 | "projects/toppy/package.json", 45 | "package.json", 46 | "package-lock.json", 47 | "CHANGELOG.md", 48 | "docs/environments/version.ts", 49 | "docs/assets/archived-versions.json" 50 | ], 51 | "message": ":fire: chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 52 | } 53 | ] 54 | ], 55 | "preset": "angular" 56 | } 57 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/position/global-position.ts: -------------------------------------------------------------------------------- 1 | import { InsidePlacement } from '../models'; 2 | import { setWH } from '../utils'; 3 | import { ToppyPosition } from './position'; 4 | 5 | interface GlobalPositionConfig { 6 | placement?: InsidePlacement; 7 | offset?: number; 8 | width?: string | number; 9 | height?: string | number; 10 | } 11 | 12 | export class GlobalPosition extends ToppyPosition { 13 | protected config: GlobalPositionConfig = { placement: InsidePlacement.CENTER, width: 100, height: 100, offset: 0 }; 14 | 15 | constructor(config: GlobalPositionConfig) { 16 | super(); 17 | this.updateConfig(config); 18 | } 19 | getPositions(hostEl?: HTMLElement) { 20 | const host = hostEl.getBoundingClientRect() as any; 21 | const src = { 22 | width: window['innerWidth'], 23 | height: window['innerHeight'] 24 | }; 25 | let { width: w, height: h } = this.config; 26 | 27 | w = setWH(src, host, 'width', w); 28 | h = setWH(src, host, 'height', h); 29 | 30 | const props = this.calc(this.config.placement, src, host); 31 | return { 32 | ...props, 33 | width: w, 34 | height: h, 35 | position: 'fixed', 36 | extra: this.config.placement 37 | }; 38 | } 39 | 40 | private calc(placement: InsidePlacement, src, host) { 41 | const [main, sub] = placement.split(''); 42 | const p: any = {}; 43 | 44 | if (main === 't') { 45 | p.top = this.config.offset; 46 | } 47 | if (main === 'b') { 48 | p.bottom = this.config.offset; 49 | } 50 | if ((main === 'l' || main === 'r' || main === 'c') && !sub) { 51 | p.top = (src.height - host.height) / 2; 52 | } 53 | 54 | if ((main === 't' || main === 'b' || main === 'c') && !sub) { 55 | p.left = (src.width - host.width) / 2; 56 | } 57 | if ((main === 'l' && !sub) || sub === 'l') { 58 | p.left = this.config.offset; 59 | } 60 | if ((main === 'r' && !sub) || sub === 'r') { 61 | p.right = this.config.offset; 62 | } 63 | 64 | return p; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /docs/styles/vendors/_bootstrap.scss: -------------------------------------------------------------------------------- 1 | /* Bootstrap config */ 2 | $body-bg: clr('bg'); 3 | $body-color: clr('text'); 4 | $link-color: clr('primary'); 5 | $link-hover-color: clr('primary-o'); 6 | $headings-color: clr('dark'); 7 | 8 | $theme-colors: ( 9 | primary: clr('primary'), 10 | secondary: clr('secondary'), 11 | success: clr('success'), 12 | warning: clr('warning'), 13 | danger: clr('danger'), 14 | info: clr('info'), 15 | dark: clr('dark'), 16 | alt: clr('alt'), 17 | ); 18 | $border-radius: $toppy--border-rad; 19 | $font-family-base: $toppy--font-family; 20 | $headings-font-family: $toppy--font-family-headings; 21 | $breadcrumb-margin-bottom: 0; 22 | 23 | $h1-font-size: fs('h1'); 24 | $h2-font-size: fs('h2'); 25 | $h3-font-size: fs('h3'); 26 | $h4-font-size: fs('h4'); 27 | $h5-font-size: fs('h5'); 28 | $h6-font-size: fs('h6'); 29 | $input-btn-padding-y-lg: 0.8rem; 30 | $input-btn-padding-x-lg: 1.5rem; 31 | 32 | $grid-breakpoints: $toppy--grid-breakpoints; 33 | 34 | @import '~bootstrap/scss/functions'; 35 | @import '~bootstrap/scss/variables'; 36 | @import '~bootstrap/scss/mixins'; 37 | // @import '~bootstrap/scss/print'; 38 | @import '~bootstrap/scss/reboot'; 39 | @import '~bootstrap/scss/type'; 40 | @import '~bootstrap/scss/images'; 41 | @import '~bootstrap/scss/code'; 42 | @import '~bootstrap/scss/grid'; 43 | @import '~bootstrap/scss/tables'; 44 | @import '~bootstrap/scss/forms'; 45 | @import '~bootstrap/scss/buttons'; 46 | @import '~bootstrap/scss/transitions'; 47 | //@import '~bootstrap/scss/dropdown'; 48 | //@import '~bootstrap/scss/button-group'; 49 | @import '~bootstrap/scss/input-group'; 50 | @import '~bootstrap/scss/custom-forms'; 51 | @import '~bootstrap/scss/nav'; 52 | @import '~bootstrap/scss/navbar'; 53 | @import '~bootstrap/scss/card'; 54 | //@import '~bootstrap/scss/breadcrumb'; 55 | // @import '~bootstrap/scss/pagination'; 56 | @import '~bootstrap/scss/badge'; 57 | // @import '~bootstrap/scss/jumbotron'; 58 | @import '~bootstrap/scss/alert'; 59 | //@import '~bootstrap/scss/progress'; 60 | // @import '~bootstrap/scss/media'; 61 | @import '~bootstrap/scss/list-group'; 62 | @import '~bootstrap/scss/close'; 63 | //@import '~bootstrap/scss/modal'; 64 | //@import '~bootstrap/scss/tooltip'; 65 | //@import '~bootstrap/scss/popover'; 66 | // @import '~bootstrap/scss/carousel'; 67 | @import '~bootstrap/scss/utilities'; 68 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { TemplateRef } from '@angular/core'; 2 | import { Observable, Subject } from 'rxjs'; 3 | import { filter, map } from 'rxjs/operators'; 4 | import { Content, ContentData, ContentProps, ContentType, ToppyEvent, ToppyEventName } from './models'; 5 | 6 | export function getContent(data: ContentData, props: ContentProps = {}): Content { 7 | let type: ContentType = ContentType.COMPONENT; 8 | 9 | if (typeof data === 'string' && props['hasHTML']) type = ContentType.HTML; 10 | else if (typeof data === 'string') type = ContentType.STRING; 11 | else if (data instanceof TemplateRef) type = ContentType.TEMPLATE; 12 | 13 | return { data, type, props }; 14 | } 15 | 16 | export function createId() { 17 | return Math.random() 18 | .toString(36) 19 | .substr(2, 5); 20 | } 21 | 22 | /* html dom utils */ 23 | 24 | export function cssClass(method: 'add' | 'remove', cls: string[], target: string = 'body') { 25 | document.querySelector(target).classList[method](...cls); 26 | } 27 | 28 | export function toCss(styleObj) { 29 | return Object.keys(styleObj) 30 | .map(x => `${x}:${styleObj[x]}${typeof styleObj[x] === 'number' ? 'px' : ''}`) 31 | .join(';'); 32 | } 33 | 34 | export function percentToCss(max, percentage: string): string { 35 | let number = Number(percentage.slice(0, -1)); 36 | if (number > 100) { 37 | number = 100; 38 | } 39 | return `calc(${max}px - ${100 - number}%)`; 40 | } 41 | 42 | export function setWH(src, host, key, value) { 43 | if (typeof value === 'number') { 44 | host[key] = value = Math.abs(value); 45 | } 46 | 47 | if (typeof value === 'string' && value.endsWith('%')) { 48 | value = percentToCss(src[key], value); 49 | } 50 | 51 | return value; 52 | } 53 | 54 | export const BodyEl = document.querySelector('body'); 55 | 56 | /* events */ 57 | 58 | class BusClass { 59 | private _e: Subject = new Subject(); 60 | send(from: string, name: ToppyEventName, data: any = null): void { 61 | this._e.next({ from, name, data }); 62 | } 63 | listen(from: string, name: ToppyEventName): Observable { 64 | return this._e.asObservable().pipe( 65 | filter(e => e.from === from && e.name === name), 66 | map(e => e.data) 67 | ); 68 | } 69 | 70 | stop(): void { 71 | this._e.complete(); 72 | } 73 | } 74 | export const Bus = new BusClass(); 75 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/toppy.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector } from '@angular/core'; 2 | import { DefaultConfig } from './config'; 3 | import { ContentData, ContentProps, ContentType, Inputs, InsidePlacement, TID, ToppyConfig } from './models'; 4 | import { GlobalPosition } from './position'; 5 | import { ToppyPosition } from './position/position'; 6 | import { ToppyControl } from './toppy-control'; 7 | import { Bus, createId, getContent } from './utils'; 8 | 9 | @Injectable({ 10 | providedIn: 'root' 11 | }) 12 | export class Toppy { 13 | static controls: { [key: string]: ToppyControl } = {}; 14 | private tid: TID; 15 | private inputs: Inputs = { 16 | position: null, 17 | config: DefaultConfig, 18 | content: { type: ContentType.STRING, data: 'hello', props: {} }, 19 | tid: null 20 | }; 21 | 22 | constructor(private injector: Injector) { 23 | this.inputs.position = new GlobalPosition({ placement: InsidePlacement.TOP }); 24 | } 25 | 26 | position(position: ToppyPosition): Toppy { 27 | this.inputs.position = position; 28 | return this; 29 | } 30 | 31 | config(config: Partial): Toppy { 32 | this.inputs.config = { ...DefaultConfig, ...config }; 33 | return this; 34 | } 35 | 36 | content(data: ContentData, props: ContentProps = {}): Toppy { 37 | this.inputs.content = getContent(data, props); 38 | return this; 39 | } 40 | 41 | create(key: string = null): ToppyControl { 42 | this.tid = this.inputs.tid = key || createId(); 43 | 44 | const injector = Injector.create( 45 | [ 46 | { 47 | provide: ToppyControl, 48 | deps: [ApplicationRef, ComponentFactoryResolver, Injector] 49 | } 50 | ], 51 | this.injector 52 | ); 53 | 54 | const tc = injector.get(ToppyControl); 55 | if (Toppy.controls[this.tid]) { 56 | this.tid = createId(); 57 | } 58 | this.inputs.position.init(this.tid); 59 | Toppy.controls[this.tid] = Object.assign(tc, this.inputs); 60 | return tc; 61 | } 62 | 63 | getCtrl(tid: TID): ToppyControl { 64 | return Toppy.controls[tid]; 65 | } 66 | 67 | destroy() { 68 | // tslint:disable-next-line:forin 69 | for (const key in Toppy.controls) { 70 | Toppy.controls[key].close(); 71 | } 72 | Toppy.controls = {}; 73 | Bus.stop(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /docs/app/examples/relative-position-example/relative-position-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { OutsidePlacement, RelativePosition, Toppy } from 'toppy'; 4 | import { ToppyControl } from '../../../../projects/toppy/src/lib/toppy-control'; 5 | 6 | @Component({ 7 | selector: 'app-relative-position-example', 8 | templateUrl: './relative-position-example.component.html', 9 | styles: [], 10 | providers: [Toppy] 11 | }) 12 | export class RelativePositionExampleComponent implements OnInit { 13 | @ViewChild('targetEl', { read: ElementRef }) 14 | targetEl: ElementRef; 15 | 16 | placements: { name: string; value: OutsidePlacement }[] = [ 17 | { name: 'Bottom', value: OutsidePlacement.BOTTOM }, 18 | { name: 'Bottom left', value: OutsidePlacement.BOTTOM_LEFT }, 19 | { name: 'Bottom right', value: OutsidePlacement.BOTTOM_RIGHT }, 20 | { name: 'Left', value: OutsidePlacement.LEFT }, 21 | { name: 'Left bottom', value: OutsidePlacement.LEFT_BOTTOM }, 22 | { name: 'Left top', value: OutsidePlacement.LEFT_TOP }, 23 | { name: 'Right', value: OutsidePlacement.RIGHT }, 24 | { name: 'Right bottom', value: OutsidePlacement.RIGHT_BOTTOM }, 25 | { name: 'Right top', value: OutsidePlacement.RIGHT_TOP }, 26 | { name: 'Top', value: OutsidePlacement.TOP }, 27 | { name: 'Top left', value: OutsidePlacement.TOP_LEFT }, 28 | { name: 'Top right', value: OutsidePlacement.TOP_RIGHT } 29 | ]; 30 | selectedPlacement = this.placements[0].value; 31 | private _toppyControl: ToppyControl; 32 | destroy$ = new Subject(); 33 | constructor(private toppy: Toppy) {} 34 | 35 | ngOnInit() { 36 | this._toppyControl = this.toppy 37 | .position( 38 | new RelativePosition({ 39 | placement: this.selectedPlacement, 40 | src: this.targetEl.nativeElement, 41 | autoUpdate: true 42 | }) 43 | ) 44 | .content('hello') 45 | .create(); 46 | } 47 | 48 | onOptionChange() { 49 | this._toppyControl.updatePosition({ 50 | placement: this.selectedPlacement 51 | }); 52 | } 53 | onMouseOver() { 54 | const content = this.placements.find(a => a.value === this.selectedPlacement).name; 55 | this._toppyControl.updateContent(content, { class: 'tooltip' }); 56 | this._toppyControl.open(); 57 | } 58 | onMouseLeave() { 59 | this._toppyControl.close(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/models.ts: -------------------------------------------------------------------------------- 1 | import { TemplateRef } from '@angular/core'; 2 | import { ToppyPosition } from './position/position'; 3 | 4 | export interface PositionMeta { 5 | top?: number; 6 | left?: number; 7 | bottom?: number; 8 | right?: number; 9 | height?: number | string; 10 | width?: number | string; 11 | position?: string; 12 | extra?: string; 13 | } 14 | 15 | enum p { 16 | TOP = 't', 17 | LEFT = 'l', 18 | RIGHT = 'r', 19 | BOTTOM = 'b', 20 | TOP_LEFT = 'tl', 21 | TOP_RIGHT = 'tr', 22 | BOTTOM_LEFT = 'bl', 23 | BOTTOM_RIGHT = 'br' 24 | } 25 | 26 | enum o { 27 | LEFT_TOP = 'lt', 28 | RIGHT_TOP = 'rt', 29 | LEFT_BOTTOM = 'lb', 30 | RIGHT_BOTTOM = 'rb' 31 | } 32 | 33 | enum i { 34 | CENTER = 'c' 35 | } 36 | export const OutsidePlacement = { 37 | ...p, 38 | ...o 39 | }; 40 | export const InsidePlacement = { 41 | ...p, 42 | ...i 43 | }; 44 | export type OutsidePlacement = p | o; 45 | export type InsidePlacement = p | i; 46 | 47 | export enum SlidePlacement { 48 | LEFT = 'l', 49 | RIGHT = 'r' 50 | } 51 | 52 | export interface ContainerSize { 53 | width: string | number; 54 | height: string | number; 55 | } 56 | 57 | export interface ToppyConfig { 58 | backdrop: boolean; 59 | containerClass: string; 60 | wrapperClass: string; 61 | backdropClass: string; 62 | listenWindowEvents: boolean; 63 | closeOnDocClick: boolean; 64 | bodyClass: string; 65 | closeOnEsc: boolean; 66 | windowResizeCallback: () => void; 67 | docClickCallback: () => void; 68 | } 69 | 70 | export interface ComponentType { 71 | new (...args: any[]): T; 72 | } 73 | 74 | export type TID = string; 75 | 76 | export type ToppyEventName = 't_open' | 't_close' | 't_dynpos' | 't_detach' | 't_posupdate' | 't_compins'; 77 | 78 | export interface ToppyEvent { 79 | from: TID; 80 | name: ToppyEventName; 81 | data?: any; 82 | } 83 | 84 | export const enum ContentType { 85 | STRING = 's', 86 | HTML = 'h', 87 | TEMPLATE = 't', 88 | COMPONENT = 'c' 89 | } 90 | export type ContentData = string | TemplateRef | ComponentType; 91 | export type ContentProps = { [x: string]: any } | any; 92 | 93 | export interface Content { 94 | type?: ContentType; 95 | data: ContentData; 96 | props?: ContentProps; 97 | } 98 | 99 | export interface Inputs { 100 | position: ToppyPosition | null; 101 | config: ToppyConfig; 102 | content: Content; 103 | tid: TID; 104 | } 105 | -------------------------------------------------------------------------------- /docs/app/examples/global-position-example/global-position-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewEncapsulation } from '@angular/core'; 2 | import { GlobalPosition, InsidePlacement, Toppy, ToppyControl } from 'toppy'; 3 | import { SimpleModalComponent } from '../../host-components/simple-modal/simple-modal.component'; 4 | 5 | @Component({ 6 | selector: 'app-global-position-example', 7 | templateUrl: './global-position-example.component.html', 8 | encapsulation: ViewEncapsulation.None, 9 | styles: [ 10 | ` 11 | .global-content-wrapper { 12 | background: #e91e63; 13 | padding: 1rem 2rem; 14 | border: 2px solid #cc1a57; 15 | border-radius: 3px; 16 | color: #fff; 17 | font-weight: 500; 18 | } 19 | ` 20 | ] 21 | }) 22 | export class GlobalPositionExampleComponent implements OnInit { 23 | placements: { name: string; value: InsidePlacement }[] = [ 24 | { name: 'Bottom', value: InsidePlacement.BOTTOM }, 25 | { name: 'Bottom left', value: InsidePlacement.BOTTOM_LEFT }, 26 | { name: 'Bottom right', value: InsidePlacement.BOTTOM_RIGHT }, 27 | { name: 'Left', value: InsidePlacement.LEFT }, 28 | { name: 'Right', value: InsidePlacement.RIGHT }, 29 | { name: 'Top', value: InsidePlacement.TOP }, 30 | { name: 'Top left', value: InsidePlacement.TOP_LEFT }, 31 | { name: 'Top right', value: InsidePlacement.TOP_RIGHT }, 32 | { name: 'Center', value: InsidePlacement.CENTER } 33 | ]; 34 | selectedPlacement = this.placements[8].value; 35 | private _toppyControl: ToppyControl; 36 | constructor(private toppy: Toppy) {} 37 | 38 | ngOnInit() { 39 | this._toppyControl = this.toppy 40 | .position(new GlobalPosition({ placement: this.selectedPlacement, height: 'auto', width: 'auto', offset: 10 })) 41 | .config({ 42 | docClickCallback: () => { 43 | console.log('doc click callback'); 44 | }, 45 | closeOnDocClick: true, 46 | wrapperClass: 'global-content-wrapper', 47 | backdrop: true, 48 | bodyClass: 'global-toastr' 49 | }) 50 | .content(SimpleModalComponent) 51 | .create(); 52 | } 53 | 54 | open() { 55 | const content = this.placements.find(a => a.value === this.selectedPlacement).name; 56 | this._toppyControl.updateContent(content); 57 | this._toppyControl.open(); 58 | } 59 | 60 | onOptionChange() { 61 | this._toppyControl.updatePosition({ 62 | placement: this.selectedPlacement 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /scripts/build-md.js: -------------------------------------------------------------------------------- 1 | const execa = require('execa'); 2 | const fs = require('fs-extra'); 3 | const Listr = require('listr'); 4 | const sane = require('sane'); 5 | var unified = require('unified'); 6 | var markdown = require('remark-parse'); 7 | var html = require('remark-html'); 8 | var bracketedSpans = require('remark-bracketed-spans'); 9 | const replace = require('replace-in-file'); 10 | const stringify = require('rehype-stringify'); 11 | var highlight = require('rehype-highlight'); 12 | const remarkAttr = require('remark-attr'); 13 | const remark2rehype = require('remark-rehype'); 14 | const slug = require('remark-slug'); 15 | const headings = require('remark-autolink-headings'); 16 | var raw = require('rehype-raw'); 17 | var format = require('rehype-format'); 18 | var watcher = sane('./docs/markdown', { glob: ['**/*.md'] }); 19 | 20 | const tasks = new Listr([ 21 | { 22 | title: 'fetch contents', 23 | task: ctx => { 24 | return fs.readFile('./docs/markdown/main.md', 'utf8').then(c => (ctx.contents = c)); 25 | } 26 | }, 27 | { 28 | title: 'converting markdown to html', 29 | task: ctx => { 30 | return unified() 31 | .use(markdown) 32 | .use(remarkAttr) 33 | .use(bracketedSpans) 34 | .use(slug) 35 | .use(headings, { behaviour: 'wrap' }) 36 | .use(remark2rehype, { allowDangerousHTML: true }) 37 | .use(raw) 38 | .use(format) 39 | .use(highlight) 40 | .use(stringify) 41 | .process(ctx.contents) 42 | .then(c => String(c)) 43 | .then(c => (ctx.contents = c)); 44 | } 45 | }, 46 | { 47 | title: 'file contents changed', 48 | task: ctx => { 49 | return fs.outputFile('./docs/app/utils/content/content.component.html', ctx.contents); 50 | } 51 | }, 52 | { 53 | title: 'replace braces', 54 | task: ctx => { 55 | return replace({ 56 | files: './docs/app/utils/content/content.component.html', 57 | from: [/\{/g, /\}/g], 58 | to: ['{', '}'] 59 | }).then(c => (ctx.contents = c)); 60 | } 61 | }, 62 | { 63 | title: 'replace links', 64 | task: ctx => { 65 | return replace({ 66 | files: './docs/app/utils/content/content.component.html', 67 | from: [/(span-classtoppy-icon-icon-)(.+?)--span-/g], 68 | to: [''] 69 | }).then(c => (ctx.contents = c)); 70 | } 71 | } 72 | ]); 73 | 74 | watcher.on('change', () => { 75 | tasks.run().catch(err => { 76 | console.error(err); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /docs/app/examples/drag-example/drag-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { OutsidePlacement } from '../../../../projects/toppy/src/lib/models'; 4 | import { RelativePosition, Toppy, ToppyControl } from '../../../../projects/toppy/src/public_api'; 5 | 6 | @Component({ 7 | selector: 'app-drag-example', 8 | templateUrl: './drag-example.component.html' 9 | }) 10 | export class DragExampleComponent implements OnInit { 11 | pos1 = 0; 12 | pos2 = 0; 13 | pos3 = 0; 14 | pos4 = 0; 15 | @ViewChild('el') el: ElementRef; 16 | private _toppyControl: ToppyControl; 17 | pauser = new Subject(); 18 | constructor(private toppy: Toppy) {} 19 | 20 | ngOnInit() { 21 | this._toppyControl = this.toppy 22 | .position( 23 | new RelativePosition({ 24 | placement: OutsidePlacement.TOP, 25 | src: this.el.nativeElement, 26 | width: 'auto', 27 | autoUpdate: true 28 | }) 29 | ) 30 | .content('Drag me', { class: 'tooltip' }) 31 | .create(); 32 | } 33 | 34 | ngAfterViewInit() { 35 | this.dragElement(); 36 | } 37 | reset() { 38 | this.el.nativeElement.style.left = '0px'; 39 | this.el.nativeElement.style.top = '0px'; 40 | } 41 | onMouseOver() { 42 | this._toppyControl.open(); 43 | } 44 | onMouseLeave() { 45 | this._toppyControl.close(); 46 | } 47 | dragMouseDown(e) { 48 | e = e || window.event; 49 | e.preventDefault(); 50 | // get the mouse cursor position at startup: 51 | this.pos3 = e.clientX; 52 | this.pos4 = e.clientY; 53 | document.onmouseup = this.closeDragElement.bind(this); 54 | // call a function whenever the cursor moves: 55 | document.onmousemove = this.elementDrag.bind(this); 56 | } 57 | 58 | elementDrag(e) { 59 | e = e || window.event; 60 | e.preventDefault(); 61 | // calculate the new cursor position: 62 | this.pos1 = this.pos3 - e.clientX; 63 | this.pos2 = this.pos4 - e.clientY; 64 | this.pos3 = e.clientX; 65 | this.pos4 = e.clientY; 66 | this.el.nativeElement.style.top = this.el.nativeElement.offsetTop - this.pos2 + 'px'; 67 | this.el.nativeElement.style.left = this.el.nativeElement.offsetLeft - this.pos1 + 'px'; 68 | } 69 | 70 | closeDragElement() { 71 | document.onmouseup = null; 72 | document.onmousemove = null; 73 | } 74 | 75 | dragElement() { 76 | this.el.nativeElement.onmousedown = this.dragMouseDown.bind(this); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /docs/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientModule } from '@angular/common/http'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { BrowserModule } from '@angular/platform-browser'; 5 | import { ToppyModule } from 'toppy'; 6 | import { AppComponent } from './app.component'; 7 | import { ControlExampleComponent } from './examples/control-example/control-example.component'; 8 | import { DragExampleComponent } from './examples/drag-example/drag-example.component'; 9 | import { DropdownExampleComponent } from './examples/dropdown-example/dropdown-example.component'; 10 | import { DynamicTextExampleComponent } from './examples/dynamic-text-example/dynamic-text-example.component'; 11 | import { FullscreenPositionExampleComponent } from './examples/fullscreen-position-example/fullscreen-position-example.component'; 12 | import { GlobalPositionExampleComponent } from './examples/global-position-example/global-position-example.component'; 13 | import { ModalExampleComponent } from './examples/modal-example/modal-example.component'; 14 | import { RelativePositionExampleComponent } from './examples/relative-position-example/relative-position-example.component'; 15 | import { RibbonExampleComponent } from './examples/ribbon-example/ribbon-example.component'; 16 | import { SlidePositionExampleComponent } from './examples/slide-position-example/slide-position-example.component'; 17 | import { HeroScreenComponent } from './host-components/hero-screen/hero-screen.component'; 18 | import { SimpleListComponent } from './host-components/simple-list/simple-list.component'; 19 | import { SimpleModalComponent } from './host-components/simple-modal/simple-modal.component'; 20 | import { TooltipComponent } from './host-components/tooltip/tooltip.component'; 21 | import { TestComponent } from './test/test.component'; 22 | import { ContentComponent } from './utils/content/content.component'; 23 | import { ScrollSpyDirective } from './utils/scollspy.directive'; 24 | import { SectionComponent } from './utils/section/section.component'; 25 | import { SubSectionComponent } from './utils/sub-section/sub-section.component'; 26 | 27 | @NgModule({ 28 | declarations: [ 29 | AppComponent, 30 | TestComponent, 31 | SectionComponent, 32 | SubSectionComponent, 33 | RelativePositionExampleComponent, 34 | TooltipComponent, 35 | SimpleModalComponent, 36 | SimpleListComponent, 37 | GlobalPositionExampleComponent, 38 | SlidePositionExampleComponent, 39 | FullscreenPositionExampleComponent, 40 | DragExampleComponent, 41 | HeroScreenComponent, 42 | ModalExampleComponent, 43 | ContentComponent, 44 | DynamicTextExampleComponent, 45 | DropdownExampleComponent, 46 | RibbonExampleComponent, 47 | ControlExampleComponent, 48 | ScrollSpyDirective 49 | ], 50 | imports: [BrowserModule, FormsModule, HttpClientModule, ToppyModule], 51 | providers: [], 52 | entryComponents: [TestComponent, TooltipComponent, SimpleModalComponent, SimpleListComponent, HeroScreenComponent], 53 | bootstrap: [AppComponent], 54 | exports: [] 55 | }) 56 | export class AppModule {} 57 | -------------------------------------------------------------------------------- /docs/app/examples/dropdown-example/dropdown-example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, TemplateRef, ViewChild } from '@angular/core'; 2 | import { OutsidePlacement } from '../../../../projects/toppy/src/lib/models'; 3 | import { RelativePosition, Toppy, ToppyControl } from '../../../../projects/toppy/src/public_api'; 4 | 5 | @Component({ 6 | selector: 'app-dropdown-example', 7 | templateUrl: './dropdown-example.component.html', 8 | styleUrls: ['./dropdown-example.component.scss'] 9 | }) 10 | export class DropdownExampleComponent implements OnInit { 11 | @ViewChild('el') el: ElementRef; 12 | @ViewChild('el2') el2: ElementRef; 13 | private _toppyControl: ToppyControl; 14 | private _toppyControl2: ToppyControl; 15 | @ViewChild('tpl', { read: TemplateRef }) tpl: TemplateRef; 16 | @ViewChild('tpl2', { read: TemplateRef }) tpl2: TemplateRef; 17 | items = [ 18 | { name: 'Beer', icon: 'icons8-beer-50' }, 19 | { name: 'Coffee', icon: 'icons8-cafe-50' }, 20 | { name: 'Cocktail', icon: 'icons8-cocktail-50' }, 21 | { name: 'Cola', icon: 'icons8-cola-50' }, 22 | { name: 'Tequila', icon: 'icons8-tequila-shot-50' }, 23 | { name: 'Whiskey', icon: 'icons8-whiskey-50' } 24 | ]; 25 | eatables = [ 26 | { name: 'French fries', icon: 'icons8-french-fries-50' }, 27 | { name: 'Fried chicken', icon: 'icons8-fried-chicken-50' }, 28 | { name: 'Hamburger', icon: 'icons8-hamburger-50' }, 29 | { name: 'Pizza', icon: 'icons8-pizza-50' }, 30 | { name: 'Sandwich', icon: 'icons8-sandwich-50' } 31 | ]; 32 | selectedData; 33 | selectedEatable; 34 | isOpen = false; 35 | constructor(private toppy: Toppy) { 36 | this.selectedData = this.items[0]; 37 | this.selectedEatable = this.eatables[0]; 38 | } 39 | 40 | ngOnInit() { 41 | this._toppyControl = this.toppy 42 | .position( 43 | new RelativePosition({ 44 | placement: OutsidePlacement.BOTTOM, 45 | src: this.el.nativeElement, 46 | width: '100%', 47 | autoUpdate: true 48 | }) 49 | ) 50 | .config({ 51 | closeOnDocClick: true, 52 | docClickCallback: () => { 53 | this.isOpen = false; 54 | } 55 | }) 56 | .content(this.tpl) 57 | .create('sonia'); 58 | 59 | this._toppyControl2 = this.toppy 60 | .position( 61 | new RelativePosition({ 62 | placement: OutsidePlacement.TOP, 63 | src: this.el2.nativeElement, 64 | width: 'auto', 65 | autoUpdate: true 66 | }) 67 | ) 68 | .config({ 69 | docClickCallback: () => { 70 | this.isOpen = false; 71 | } 72 | }) 73 | .content(this.tpl2) 74 | .create(); 75 | } 76 | 77 | open() { 78 | this.isOpen = true; 79 | this._toppyControl.open(); 80 | } 81 | open2() { 82 | this._toppyControl2.open(); 83 | } 84 | close() { 85 | this.isOpen = false; 86 | this._toppyControl.close(); 87 | } 88 | close2() { 89 | this._toppyControl2.close(); 90 | } 91 | select(item) { 92 | this.selectedData = item; 93 | this.close(); 94 | } 95 | selectEatable(item) { 96 | this.selectedEatable = item; 97 | this.close2(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Toppy - Overlay library for Angular 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /docs/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Component, ElementRef, ViewChild } from '@angular/core'; 3 | import { fromEvent } from 'rxjs'; 4 | import { TOPPY_VERSION } from '../environments/version'; 5 | // import { Toppy } from 'toppy'; 6 | import { code } from './codes'; 7 | 8 | @Component({ 9 | selector: 'app-root', 10 | templateUrl: './app.component.html' 11 | }) 12 | export class AppComponent { 13 | latestVersion = ''; 14 | versions = []; 15 | title = 'toppy-app'; 16 | selectedVersion = ''; 17 | currentVersion = TOPPY_VERSION; 18 | @ViewChild('el', { read: ElementRef }) 19 | el: ElementRef; 20 | ins; 21 | code = code; 22 | constructor(private http: HttpClient) { 23 | this.http.get('./assets/archived-versions.json').subscribe(data => { 24 | this.versions = Object.keys(data) 25 | .filter(a => a !== 'undefined') 26 | .sort((a, b) => ('' + b).localeCompare(a)); 27 | this.latestVersion = this.versions[0]; 28 | this.getCurrentVerison(); 29 | }); 30 | } 31 | currentSection = ''; 32 | 33 | onSectionChange(sectionId: string) { 34 | this.currentSection = sectionId; 35 | } 36 | 37 | scrollTo(section) { 38 | this.currentSection = section; 39 | document.querySelector('#' + section).scrollIntoView(); 40 | } 41 | 42 | ngOnInit() { 43 | this.onScroll(); 44 | } 45 | 46 | ngafterViewInit() { 47 | this.getCurrentVerison(); 48 | } 49 | 50 | getCurrentVerison() { 51 | const parts = window.location.pathname.split('/').filter(a => a.length > 0); 52 | if (parts.length === 2) { 53 | this.selectedVersion = parts[1]; 54 | } else { 55 | this.selectedVersion = this.latestVersion; 56 | } 57 | } 58 | 59 | onVersionChange(version) { 60 | (window as any).location = `https://lokesh-coder.github.io/toppy/${version}`; 61 | } 62 | 63 | onScroll() { 64 | fromEvent(window, 'scroll') 65 | .pipe() 66 | .subscribe(res => { 67 | const heroHeight = document.querySelector('.hero').getBoundingClientRect().height; 68 | const scrolledTo = document.documentElement.scrollTop; 69 | if (scrolledTo >= heroHeight) { 70 | document.querySelector('body').classList.add('sidebar-fixed'); 71 | } else { 72 | document.querySelector('body').classList.remove('sidebar-fixed'); 73 | } 74 | }); 75 | } 76 | 77 | tweet(e) { 78 | const getWindowOptions = function() { 79 | const width = 500; 80 | const height = 350; 81 | const left = window.innerWidth / 2 - width / 2; 82 | const top = window.innerHeight / 2 - height / 2; 83 | 84 | return ['resizable,scrollbars,status', 'height=' + height, 'width=' + width, 'left=' + left, 'top=' + top].join(); 85 | }; 86 | // const text = encodeURIComponent('Hey everyone, come & see how good I look!'); 87 | const shareUrl = `https://twitter.com/intent/tweet?hashtags=angular&original_referer=http%3A%2F%2Flocalhost%3A4200%2F&ref_src=twsrc%5Etfw&text=Cute%20overlay%20library%20for%20Angular%20-%20tooltips%2C%20modals%2C%20toastr%2C%20menu%2C%20dropdowns%2C%20alerts%2C%20popovers%2C%20sidebar%20and%20more...%20&tw_p=tweetbutton&url=https%3A%2F%2Flokesh-coder.github.io%2Ftoppy%2F&via=lokesh_coder`; 88 | 89 | e.preventDefault(); 90 | const win = window.open(shareUrl, 'ShareOnTwitter', getWindowOptions()); 91 | win.opener = null; // 2 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /docs/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-redundant-jsdoc": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "no-output-on-prefix": true, 121 | "use-input-property-decorator": true, 122 | "use-output-property-decorator": true, 123 | "use-host-property-decorator": true, 124 | "no-input-rename": true, 125 | "no-output-rename": true, 126 | "use-life-cycle-interface": true, 127 | "use-pipe-transform-interface": true, 128 | "component-class-suffix": true, 129 | "directive-class-suffix": true 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/toppy.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | ChangeDetectionStrategy, 4 | ChangeDetectorRef, 5 | Component, 6 | ComponentFactoryResolver, 7 | ElementRef, 8 | Injector, 9 | OnDestroy, 10 | OnInit, 11 | ViewChild, 12 | ViewContainerRef 13 | } from '@angular/core'; 14 | import { Observable, Subject } from 'rxjs'; 15 | import { startWith, takeUntil, tap } from 'rxjs/operators'; 16 | import { Content, ContentType, TID, ToppyConfig } from './models'; 17 | import { ToppyPosition } from './position/position'; 18 | import { Bus, cssClass, toCss } from './utils'; 19 | 20 | @Component({ 21 | // tslint:disable-next-line:component-selector 22 | selector: 'toppy', 23 | templateUrl: './template.html', 24 | styleUrls: ['./styles.scss'], 25 | changeDetection: ChangeDetectionStrategy.OnPush 26 | }) 27 | export class ToppyComponent implements OnInit, AfterViewInit, OnDestroy { 28 | @ViewChild('compOutlet', { read: ViewContainerRef }) compOutlet: ViewContainerRef; 29 | content: Content = { 30 | type: ContentType.STRING, 31 | data: '', 32 | props: {} 33 | }; 34 | config: ToppyConfig; 35 | position: ToppyPosition; 36 | tid: TID; 37 | el: HTMLElement | any; 38 | wrapperEl: HTMLElement | any; 39 | extra: string; 40 | pinj: any; 41 | compInstance; 42 | private die: Subject<1> = new Subject(); 43 | 44 | constructor( 45 | public inj: Injector, 46 | private cd: ChangeDetectorRef, 47 | private compResolver: ComponentFactoryResolver, 48 | private elRef: ElementRef 49 | ) { 50 | this.pinj = Injector; 51 | } 52 | 53 | ngOnInit() { 54 | this.el = this.elRef.nativeElement; 55 | this.wrapperEl = this.el.querySelector('.t-wrapper'); 56 | let cls = ['t-container', this.config.containerClass, this.position.getClassName()]; 57 | if (this.config.closeOnDocClick) { 58 | cls = cls.concat(['no-pointers']); 59 | } 60 | this.el.setAttribute('data-tid', this.tid); 61 | cssClass('add', cls, `[data-tid='${[this.tid]}']`); 62 | cssClass('add', [this.config.bodyClass]); 63 | } 64 | 65 | ngAfterViewInit() { 66 | this.listenPos().subscribe(); 67 | if (this.content.type === ContentType.COMPONENT) { 68 | this.compInstance = this.setComponent(this.content.props); 69 | Bus.send(this.tid, 't_compins', this.compInstance); 70 | } 71 | } 72 | 73 | setComponent(props) { 74 | const compRef = this.compOutlet.createComponent( 75 | this.compResolver.resolveComponentFactory(this.content.data as any) 76 | ); 77 | Object.assign(compRef.instance, props); 78 | compRef.changeDetectorRef.detectChanges(); 79 | return compRef.instance; 80 | } 81 | 82 | updateTextContent(data: string): void { 83 | if (this.content.type === ContentType.STRING) { 84 | this.content.data = data; 85 | this.cd.detectChanges(); 86 | } 87 | } 88 | 89 | ngOnDestroy(): void { 90 | cssClass('remove', [this.config.bodyClass]); 91 | this.die.next(1); 92 | Bus.send(this.tid, 't_detach'); 93 | } 94 | 95 | private listenPos(): Observable { 96 | return Bus.listen(this.tid, 't_dynpos').pipe( 97 | startWith(1), 98 | takeUntil(this.die), 99 | tap(e => { 100 | if (!e || !e.x) return this.setPos(); 101 | const coords = { left: e.x, top: e.y }; 102 | this.wrapperEl.style = toCss(coords); 103 | }) 104 | ); 105 | } 106 | 107 | private setPos(): void { 108 | const { extra, ...coords } = this.position.getPositions(this.wrapperEl); 109 | if (this.extra !== extra) { 110 | this.extra = extra; 111 | this.cd.detectChanges(); 112 | } 113 | Object.assign(coords, { visibility: 'visible', opacity: '1' }); 114 | this.wrapperEl.style = toCss(coords); 115 | Bus.send(this.tid, 't_posupdate'); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/position/relative-position.ts: -------------------------------------------------------------------------------- 1 | import { OutsidePlacement, PositionMeta } from '../models'; 2 | import { Bus, setWH } from '../utils'; 3 | import { ToppyPosition } from './position'; 4 | 5 | interface RelativePositionConfig { 6 | src: HTMLElement; 7 | placement?: OutsidePlacement; 8 | autoUpdate?: boolean; 9 | width?: string | number; 10 | height?: string | number; 11 | } 12 | 13 | export class RelativePosition extends ToppyPosition { 14 | protected config: RelativePositionConfig = { 15 | src: null, 16 | placement: OutsidePlacement.TOP, 17 | autoUpdate: false, 18 | width: 'auto', 19 | height: 'auto' 20 | }; 21 | obs: MutationObserver; 22 | constructor(config: RelativePositionConfig) { 23 | super(); 24 | this.updateConfig(config); 25 | } 26 | init(tid: string): void { 27 | if (this.config.autoUpdate) this.listenDrag(tid); 28 | } 29 | 30 | getPositions(targetEl: HTMLElement): Pick { 31 | const s = this.getCoords(this.config.src); 32 | const h = this.getCoords(targetEl); 33 | let { width: w, height: ht } = this.config; 34 | 35 | w = setWH(s, h, 'width', w); 36 | ht = setWH(s, h, 'height', ht); 37 | 38 | const { pos, props } = this.calculatePos(this.config.placement, s, h); 39 | return { ...this.round(props), width: w, height: ht, extra: pos }; 40 | } 41 | 42 | private getCoords(elem: HTMLElement): PositionMeta { 43 | return elem.getBoundingClientRect(); 44 | } 45 | 46 | private calc(placement: OutsidePlacement, src, host): object { 47 | const [main, sub] = placement.split(''); 48 | const p = { left: 0, top: 0 }; 49 | if ((main === 't' || main === 'b') && !sub) { 50 | p.left = src.left + (src.width - host.width) / 2; 51 | } 52 | 53 | if ((main === 't' || main === 'b') && sub) { 54 | p.left = src.left; 55 | } 56 | if ((main === 't' || main === 'b') && sub === 'r') { 57 | p.left = src.left + src.width - host.width; 58 | } 59 | if (main === 'l') { 60 | p.left = src.left - host.width; 61 | } 62 | if (main === 'r') { 63 | p.left = src.right; 64 | } 65 | 66 | if (main === 't') { 67 | p.top = src.top - host.height; 68 | } 69 | if (main === 'b') { 70 | p.top = src.top + src.height; 71 | } 72 | if (main === 'l' || main === 'r') { 73 | p.top = src.top + (src.height - host.height) / 2; 74 | } 75 | if (sub === 't' && (main === 'l' || main === 'r')) { 76 | p.top = src.top; 77 | } 78 | if (sub === 'b' && (main === 'l' || main === 'r')) { 79 | p.top = src.top + src.height - host.height; 80 | } 81 | return p; 82 | } 83 | 84 | private calculatePos(pos, s, h, c = true): { [x: string]: any } { 85 | const props = this.calc(pos, s, h); 86 | 87 | if (c && this.config.autoUpdate && this.isOverflowed({ ...props, width: h.width, height: h.height })) { 88 | return this.calculatePos(this.nextPosition(pos), s, h, false); 89 | } 90 | 91 | return { pos, props }; 92 | } 93 | 94 | private isOverflowed(props: { [x: string]: any }): boolean { 95 | const { innerHeight, innerWidth } = window; 96 | props.bottom = props.top + props.height; 97 | props.right = props.left + props.width; 98 | return props.bottom > innerHeight || props.top <= 0 || props.left <= 0 || props.right > innerWidth; 99 | } 100 | 101 | private nextPosition(current: OutsidePlacement): string { 102 | const placements = ['t', 'b', 'l', 'r', 'tl', 'bl', 'tr', 'br', 'lt', 'rt', 'lb', 'rb']; 103 | 104 | const index = placements.indexOf(current); 105 | const even = index % 2 === 0; 106 | return even ? placements[index + 1] : placements[index - 1]; 107 | } 108 | 109 | private round(props: object): object { 110 | Object.keys(props).forEach(x => (props[x] = Math.round(props[x]))); 111 | return props; 112 | } 113 | 114 | private listenDrag(tid: string): void { 115 | if (this.obs) this.obs.disconnect(); 116 | this.obs = new MutationObserver(mutationsList => { 117 | for (const mutation of mutationsList) { 118 | if (mutation.type === 'attributes') Bus.send(tid, 't_dynpos'); 119 | } 120 | }); 121 | 122 | this.obs.observe(this.config.src, { 123 | attributeFilter: ['style'] 124 | }); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toppy-app", 3 | "version": "2.3.4", 4 | "description": "Overlay library for Angular 7+", 5 | "keywords": [ 6 | "angular", 7 | "dropdown", 8 | "modal", 9 | "overlay", 10 | "popover", 11 | "popup", 12 | "sidebar", 13 | "tooltip" 14 | ], 15 | "homepage": "https://github.com/lokesh-coder/toppy", 16 | "bugs": { 17 | "url": "https://github.com/lokesh-coder/toppy/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/lokesh-coder/toppy" 22 | }, 23 | "license": "MIT", 24 | "author": { 25 | "name": "Lokesh Rajendran", 26 | "email": "mexican.dirtyfellow@gmail.com", 27 | "url": "https://github.com/lokesh-coder" 28 | }, 29 | "scripts": { 30 | "archive:app": "node ./scripts/archive.js", 31 | "build:app": "ng build --prod --baseHref=/toppy/", 32 | "build:lib": "ng build toppy", 33 | "e2e": "ng e2e", 34 | "lint": "ng lint", 35 | "ng": "ng", 36 | "postpublish": "node ./scripts/gh-pages.js", 37 | "release": "semantic-release", 38 | "start": "ng serve", 39 | "test": "npm run test:lib", 40 | "test:app": "ng test --project toppy-app", 41 | "test:lib": "ng test --project toppy", 42 | "travis-deploy-once": "travis-deploy-once" 43 | }, 44 | "dependencies": { 45 | "@angular/animations": "~7.1.0", 46 | "@angular/common": "~7.1.0", 47 | "@angular/compiler": "~7.1.0", 48 | "@angular/core": "~7.1.0", 49 | "@angular/forms": "~7.1.0", 50 | "@angular/http": "~7.1.0", 51 | "@angular/platform-browser": "~7.1.0", 52 | "@angular/platform-browser-dynamic": "~7.1.0", 53 | "@angular/router": "~7.1.0", 54 | "@ngx-prism/core": "^2.0.1", 55 | "@types/prismjs": "^1.9.0", 56 | "bootstrap": "^4.1.3", 57 | "core-js": "^2.5.7", 58 | "date-fns": "^1.29.0", 59 | "listr": "^0.14.3", 60 | "lodash-es": "^4.17.11", 61 | "npm-registry-client": "^8.6.0", 62 | "pretty-checkbox": "^3.0.3", 63 | "prismjs": "^1.15.0", 64 | "remark-bracketed-spans": "^3.0.0", 65 | "remark-custom-blocks": "^2.3.1", 66 | "rxjs": "~6.3.3", 67 | "zone.js": "~0.8.26" 68 | }, 69 | "devDependencies": { 70 | "@angular-devkit/build-angular": "~0.11.0", 71 | "@angular-devkit/build-ng-packagr": "~0.11.0", 72 | "@angular/cli": "~7.1.0", 73 | "@angular/compiler-cli": "~7.1.0", 74 | "@angular/language-service": "~7.1.0", 75 | "@commitlint/cli": "7.2.1", 76 | "@commitlint/config-conventional": "7.1.2", 77 | "@commitlint/travis-cli": "7.2.1", 78 | "@semantic-release/changelog": "3.0.1", 79 | "@semantic-release/exec": "3.3.0", 80 | "@semantic-release/git": "7.0.5", 81 | "@semantic-release/github": "^5.2.5", 82 | "@semantic-release/release-notes-generator": "^7.1.4", 83 | "@types/jasmine": "~3.3.0", 84 | "@types/jasminewd2": "~2.0.6", 85 | "@types/node": "~10.12.10", 86 | "angular-cli-ghpages": "^0.5.3", 87 | "codelyzer": "~4.5.0", 88 | "conventional-changelog": "3.0.5", 89 | "fs-extra": "^7.0.1", 90 | "gh-pages": "^2.0.1", 91 | "github-release-notes": "0.17.0", 92 | "husky": "^1.2.0", 93 | "jasmine-core": "~3.3.0", 94 | "jasmine-spec-reporter": "~4.2.1", 95 | "json": "^9.0.6", 96 | "karma": "~3.1.1", 97 | "karma-chrome-launcher": "~2.2.0", 98 | "karma-coverage-istanbul-reporter": "~2.0.4", 99 | "karma-helpful-reporter": "^0.3.4", 100 | "karma-jasmine": "~2.0.1", 101 | "karma-jasmine-html-reporter": "^1.4.0", 102 | "karma-viewport": "^1.0.2", 103 | "ng-packagr": "^4.4.0", 104 | "nodemon": "^1.18.7", 105 | "protractor": "~5.4.1", 106 | "rehype-format": "^2.3.1", 107 | "rehype-highlight": "^2.2.1", 108 | "rehype-raw": "^4.0.0", 109 | "rehype-stringify": "^5.0.0", 110 | "remark-attr": "^0.8.0", 111 | "remark-autolink-headings": "^5.1.0", 112 | "remark-highlight.js": "^5.0.0", 113 | "remark-html": "^9.0.0", 114 | "remark-rehype": "^4.0.0", 115 | "remark-slug": "^5.1.1", 116 | "replace-in-file": "^3.4.2", 117 | "sane": "^4.0.2", 118 | "semantic-release": "^15.12.2", 119 | "stylelint": "^9.9.0", 120 | "stylelint-config-recommended-scss": "^3.2.0", 121 | "stylelint-scss": "^3.4.0", 122 | "travis-deploy-once": "5.0.9", 123 | "ts-node": "~7.0.1", 124 | "tsickle": "0.34.0", 125 | "tslib": "^1.9.3", 126 | "tslint": "~5.11.0", 127 | "typescript": "~3.1.6" 128 | }, 129 | "husky": { 130 | "hooks": { 131 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /projects/toppy/src/tests/utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { Component, TemplateRef, ViewChild } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { Subject } from 'rxjs'; 4 | import { takeUntil } from 'rxjs/operators'; 5 | import { ContentType } from '../lib/models'; 6 | import { Bus, getContent } from '../lib/utils'; 7 | 8 | @Component({ 9 | selector: 'lib-test-component', 10 | template: ` 11 |

Hello

12 | I am a template! 13 | ` 14 | }) 15 | export class TestComponent { 16 | @ViewChild('tpl', { read: TemplateRef }) tpl: TemplateRef; 17 | } 18 | 19 | describe('@ Utils', () => { 20 | let component: TestComponent = null; 21 | let fixture: ComponentFixture = null; 22 | beforeEach(async(() => { 23 | TestBed.configureTestingModule({ 24 | declarations: [TestComponent], 25 | providers: [] 26 | }).compileComponents(); 27 | fixture = TestBed.createComponent(TestComponent); 28 | component = fixture.componentInstance; 29 | })); 30 | afterEach(function() { 31 | fixture.destroy(); 32 | document.body.removeChild(fixture.debugElement.nativeElement); 33 | }); 34 | 35 | describe('#getContent', () => { 36 | it('should return as string type', () => { 37 | const result = getContent('hello'); 38 | expect(result).toEqual({ data: 'hello', props: {}, type: ContentType.STRING }); 39 | }); 40 | it('should return html type', () => { 41 | const result = getContent('
Hello
', { hasHTML: true }); 42 | expect(result).toEqual({ 43 | data: '
Hello
', 44 | props: { hasHTML: true }, 45 | type: ContentType.HTML 46 | }); 47 | }); 48 | it('should return component type', () => { 49 | const result = getContent(fixture as any); 50 | expect(result as any).toEqual({ data: fixture, props: {}, type: ContentType.COMPONENT }); 51 | }); 52 | it('should return component type with props', () => { 53 | const result: any = getContent(fixture as any, { name: 'john' }); 54 | expect(result).toEqual({ 55 | data: fixture, 56 | props: { name: 'john' }, 57 | type: ContentType.COMPONENT 58 | } as any); 59 | }); 60 | it('should return component type with overlay id', () => { 61 | const result = getContent(fixture as any, { id: 'XYZ' }); 62 | expect(result as any).toEqual({ data: fixture, props: { id: 'XYZ' }, type: ContentType.COMPONENT }); 63 | }); 64 | it('should return template type', () => { 65 | const result = getContent(component.tpl, { id: 'ABC' }); 66 | expect(result as any).toEqual({ data: component.tpl, type: ContentType.TEMPLATE, props: { id: 'ABC' } }); 67 | }); 68 | }); 69 | describe('#BusClass', () => { 70 | let die: Subject; 71 | // spyOn(Bus,'stop').and.callFake(); 72 | beforeEach(() => { 73 | die = new Subject(); 74 | Bus['_e'] = new Subject(); 75 | }); 76 | afterEach(() => { 77 | die.next(true); 78 | die.complete(); 79 | Bus.stop(); 80 | }); 81 | afterAll(() => { 82 | Bus['_e'] = new Subject(); 83 | }); 84 | it('should send event on calling `sent` method', done => { 85 | Bus.listen('abc', 't_open') 86 | .pipe(takeUntil(die)) 87 | .subscribe(data => { 88 | expect(data).toEqual({ test: true }); 89 | done(); 90 | }); 91 | Bus.send('abc', 't_open', { test: true }); 92 | }); 93 | it('should send multiple event on calling many `sent` method', done => { 94 | const spy = jasmine.createSpy('spy').and.callThrough(); 95 | Bus.listen('xyz', 't_detach') 96 | .pipe(takeUntil(die)) 97 | .subscribe(() => { 98 | spy(); 99 | done(); 100 | }); 101 | Bus.send('xyz', 't_detach', `qwerty`); 102 | Bus.send('xyz', 't_detach', `home`); 103 | Bus.send('xyz', 't_detach', `Bakery`); 104 | expect(spy.calls.count()).toEqual(3); 105 | }); 106 | it('should complete the emission on calling `stop` method', done => { 107 | const spy = jasmine.createSpy('spy').and.callThrough(); 108 | Bus.listen('xyz', 't_dynpos') 109 | .pipe(takeUntil(die)) 110 | .subscribe( 111 | data => { 112 | spy(); 113 | }, 114 | null, 115 | () => { 116 | done(); 117 | } 118 | ); 119 | Bus.send('xyz', 't_dynpos', `qwerty`); 120 | Bus.send('xyz', 't_dynpos', `home`); 121 | Bus.stop(); 122 | Bus.send('xyz', 't_dynpos', `Bakery`); 123 | expect(spy.calls.count()).toEqual(2); 124 | }); 125 | }); 126 | }); 127 | -------------------------------------------------------------------------------- /projects/toppy/src/lib/toppy-control.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApplicationRef, 3 | ComponentFactory, 4 | ComponentFactoryResolver, 5 | ComponentRef, 6 | Injector, 7 | ViewRef 8 | } from '@angular/core'; 9 | import { animationFrameScheduler, fromEvent, merge as mergeObs, Observable, Subject } from 'rxjs'; 10 | import { debounceTime, distinctUntilChanged, filter, map, observeOn, skipWhile, takeUntil, tap } from 'rxjs/operators'; 11 | import { Content, ContentData, ContentProps, TID, ToppyConfig, ToppyEventName } from './models'; 12 | import { ToppyPosition } from './position/position'; 13 | import { ToppyComponent } from './toppy.component'; 14 | import { BodyEl, Bus, getContent } from './utils'; 15 | 16 | export class ToppyControl { 17 | position: ToppyPosition; 18 | config: ToppyConfig; 19 | content: Content; 20 | tid: TID; 21 | comp: ToppyComponent; 22 | updateTextContent: Subject = new Subject(); 23 | hostView: ViewRef; 24 | compRef: ComponentRef; 25 | 26 | private viewEl: HTMLElement; 27 | private isOpen = false; 28 | private compFac: ComponentFactory; 29 | private die: Subject<1> = new Subject(); 30 | 31 | constructor( 32 | private appRef: ApplicationRef, 33 | private compResolver: ComponentFactoryResolver, 34 | private injector: Injector 35 | ) { 36 | this.updateTextContent.subscribe(content => { 37 | if (this.isOpen) this.comp.updateTextContent(content); 38 | }); 39 | } 40 | 41 | open(): void { 42 | if (this.isOpen) return; 43 | 44 | this.attach(); 45 | if (this.viewEl) { 46 | mergeObs(this.onDocumentClick(), this.onWindowResize(), this.onEscClick()).subscribe(); 47 | setTimeout(() => Bus.send(this.tid, 't_dynpos'), 1); 48 | } 49 | 50 | Bus.send(this.tid, 't_open'); 51 | this.isOpen = true; 52 | } 53 | 54 | close(): void { 55 | if (!this.isOpen) return; 56 | 57 | this.dettach(); 58 | this.die.next(1); 59 | Bus.send(this.tid, 't_close'); 60 | this.isOpen = false; 61 | } 62 | 63 | toggle(): void { 64 | return this.isOpen ? this.close() : this.open(); 65 | } 66 | 67 | onEscClick(): Observable { 68 | return fromEvent(BodyEl, 'keydown').pipe( 69 | takeUntil(this.die), 70 | skipWhile(() => !this.config.closeOnEsc), 71 | filter((e: any) => (e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27) && e.target.nodeName === 'BODY'), 72 | tap(e => e.preventDefault()), 73 | map(e => e.target), 74 | tap(() => this.close()) 75 | ); 76 | } 77 | 78 | onDocumentClick(): Observable { 79 | return fromEvent(this.viewEl, 'click').pipe( 80 | takeUntil(this.die), 81 | map((e: any) => e.target), 82 | skipWhile(() => !this.config.closeOnDocClick), 83 | filter(this.isNotHostElement.bind(this)), 84 | tap(() => { 85 | this.config.docClickCallback(); 86 | this.close(); 87 | }) 88 | ); 89 | } 90 | 91 | onWindowResize(): Observable { 92 | const onResize = fromEvent(window, 'resize'); 93 | const onScroll = fromEvent(window, 'scroll', { passive: true }); 94 | return mergeObs(onResize, onScroll).pipe( 95 | skipWhile(() => !this.config.listenWindowEvents), 96 | takeUntil(this.die), 97 | debounceTime(5), 98 | observeOn(animationFrameScheduler), 99 | distinctUntilChanged(), 100 | tap(() => { 101 | Bus.send(this.tid, 't_dynpos'); 102 | this.config.windowResizeCallback(); 103 | }) 104 | ); 105 | } 106 | 107 | changePosition(newPosition: ToppyPosition): void { 108 | this.position = newPosition; 109 | } 110 | 111 | updatePosition(positionConfig: any): void { 112 | this.position.updateConfig(positionConfig); 113 | } 114 | 115 | updateContent(content: ContentData, props: ContentProps = {}): void { 116 | this.content = getContent(content, { ...this.content.props, ...props }); 117 | } 118 | 119 | listen(eventName: ToppyEventName) { 120 | return Bus.listen(this.tid, eventName); 121 | } 122 | 123 | private isNotHostElement(el): boolean { 124 | const wrapperEl = this.viewEl.querySelector('.t-wrapper'); 125 | return el !== wrapperEl && !wrapperEl.contains(el); 126 | } 127 | 128 | private attach(): void { 129 | /* create component */ 130 | this.compFac = this.compResolver.resolveComponentFactory(ToppyComponent); 131 | this.compRef = this.compFac.create(this.injector); 132 | this.comp = this.compRef.instance; 133 | 134 | /* assign props */ 135 | const { position, content, config, tid } = this; 136 | content.props.close = this.close.bind(this); 137 | Object.assign(this.comp, { position, content, config, tid }); 138 | 139 | /* attach view */ 140 | this.hostView = this.compRef.hostView; 141 | this.appRef.attachView(this.hostView); 142 | this.viewEl = (this.hostView as any).rootNodes[0]; 143 | BodyEl.appendChild(this.viewEl); 144 | } 145 | 146 | private dettach(): void { 147 | if (!this.hostView) return; 148 | 149 | this.appRef.detachView(this.hostView); 150 | this.compRef.destroy(); 151 | this.hostView = this.viewEl = this.comp = null; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "toppy-app": { 7 | "root": "", 8 | "sourceRoot": "docs", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "styleext": "scss", 14 | "spec": false 15 | }, 16 | "@schematics/angular:class": { 17 | "spec": false 18 | }, 19 | "@schematics/angular:directive": { 20 | "spec": false 21 | }, 22 | "@schematics/angular:guard": { 23 | "spec": false 24 | }, 25 | "@schematics/angular:module": { 26 | "spec": false 27 | }, 28 | "@schematics/angular:pipe": { 29 | "spec": false 30 | }, 31 | "@schematics/angular:service": { 32 | "spec": false 33 | } 34 | }, 35 | "architect": { 36 | "build": { 37 | "builder": "@angular-devkit/build-angular:browser", 38 | "options": { 39 | "outputPath": "dist/toppy-app", 40 | "index": "docs/index.html", 41 | "main": "docs/main.ts", 42 | "polyfills": "docs/polyfills.ts", 43 | "tsConfig": "docs/tsconfig.app.json", 44 | "assets": ["docs/favicon.ico", "docs/assets"], 45 | "styles": ["docs/styles/root.scss", "node_modules/prismjs/themes/prism.css"], 46 | "scripts": [ 47 | "node_modules/marked/lib/marked.js", 48 | "node_modules/prismjs/prism.js", 49 | "node_modules/prismjs/components/prism-css.min.js", 50 | "node_modules/prismjs/components/prism-typescript.min.js", 51 | "node_modules/prismjs/components/prism-powershell.min.js", 52 | "node_modules/prismjs/components/prism-javascript.min.js" 53 | ] 54 | }, 55 | "configurations": { 56 | "production": { 57 | "fileReplacements": [ 58 | { 59 | "replace": "docs/environments/environment.ts", 60 | "with": "docs/environments/environment.prod.ts" 61 | } 62 | ], 63 | "optimization": true, 64 | "outputHashing": "all", 65 | "sourceMap": false, 66 | "extractCss": true, 67 | "namedChunks": false, 68 | "aot": true, 69 | "extractLicenses": true, 70 | "vendorChunk": false, 71 | "buildOptimizer": true, 72 | "budgets": [ 73 | { 74 | "type": "initial", 75 | "maximumWarning": "2mb", 76 | "maximumError": "5mb" 77 | } 78 | ] 79 | } 80 | } 81 | }, 82 | "serve": { 83 | "builder": "@angular-devkit/build-angular:dev-server", 84 | "options": { 85 | "browserTarget": "toppy-app:build" 86 | }, 87 | "configurations": { 88 | "production": { 89 | "browserTarget": "toppy-app:build:production" 90 | } 91 | } 92 | }, 93 | "extract-i18n": { 94 | "builder": "@angular-devkit/build-angular:extract-i18n", 95 | "options": { 96 | "browserTarget": "toppy-app:build" 97 | } 98 | }, 99 | "test": { 100 | "builder": "@angular-devkit/build-angular:karma", 101 | "options": { 102 | "main": "docs/test.ts", 103 | "polyfills": "docs/polyfills.ts", 104 | "tsConfig": "docs/tsconfig.spec.json", 105 | "karmaConfig": "docs/karma.conf.js", 106 | "styles": ["docs/styles/root.scss"], 107 | "scripts": [], 108 | "assets": ["docs/favicon.ico", "docs/assets"] 109 | } 110 | }, 111 | "lint": { 112 | "builder": "@angular-devkit/build-angular:tslint", 113 | "options": { 114 | "tsConfig": ["docs/tsconfig.app.json", "docs/tsconfig.spec.json"], 115 | "exclude": ["**/node_modules/**"] 116 | } 117 | } 118 | } 119 | }, 120 | "toppy-app-e2e": { 121 | "root": "e2e/", 122 | "projectType": "application", 123 | "prefix": "", 124 | "architect": { 125 | "e2e": { 126 | "builder": "@angular-devkit/build-angular:protractor", 127 | "options": { 128 | "protractorConfig": "e2e/protractor.conf.js", 129 | "devServerTarget": "toppy-app:serve" 130 | }, 131 | "configurations": { 132 | "production": { 133 | "devServerTarget": "toppy-app:serve:production" 134 | } 135 | } 136 | }, 137 | "lint": { 138 | "builder": "@angular-devkit/build-angular:tslint", 139 | "options": { 140 | "tsConfig": "e2e/tsconfig.e2e.json", 141 | "exclude": ["**/node_modules/**"] 142 | } 143 | } 144 | } 145 | }, 146 | "toppy": { 147 | "root": "projects/toppy", 148 | "sourceRoot": "projects/toppy/src", 149 | "projectType": "library", 150 | "prefix": "lib", 151 | "architect": { 152 | "build": { 153 | "builder": "@angular-devkit/build-ng-packagr:build", 154 | "options": { 155 | "tsConfig": "projects/toppy/tsconfig.lib.json", 156 | "project": "projects/toppy/ng-package.json" 157 | } 158 | }, 159 | "test": { 160 | "builder": "@angular-devkit/build-angular:karma", 161 | "options": { 162 | "main": "projects/toppy/src/test.ts", 163 | "tsConfig": "projects/toppy/tsconfig.spec.json", 164 | "karmaConfig": "projects/toppy/karma.conf.js", 165 | "codeCoverage": true 166 | } 167 | }, 168 | "lint": { 169 | "builder": "@angular-devkit/build-angular:tslint", 170 | "options": { 171 | "tsConfig": ["projects/toppy/tsconfig.lib.json", "projects/toppy/tsconfig.spec.json"], 172 | "exclude": ["**/node_modules/**"] 173 | } 174 | } 175 | } 176 | } 177 | }, 178 | "defaultProject": "toppy-app" 179 | } 180 | -------------------------------------------------------------------------------- /docs/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | Toppy 8 |
9 |
Tiny Overlay library for Angular apps. Easy and simple API
10 |
11 | tooltip modal sidemenu dropdowns 12 | popup menu toaster alerts datepicker 13 | popovers 14 |
15 |
16 |
17 |
18 | 19 |
20 | 21 | 25 |
26 |
27 | 28 | 32 | 33 |
34 |
35 | 36 | 40 | 41 |
42 |
43 | 44 | 48 | 49 |
50 |
51 | 56 |
57 | 71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | 122 |
123 |
124 |
125 |
126 | 127 |
128 |
129 |
130 |
131 | 134 | -------------------------------------------------------------------------------- /projects/toppy/src/tests/positions/global-position.spec.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { InsidePlacement } from '../../lib/models'; 4 | import { GlobalPosition } from '../../lib/position'; 5 | 6 | describe('@ GlobalPosition', () => { 7 | let targetElement: HTMLElement; 8 | let hostElement: HTMLElement; 9 | let ww; 10 | let wh; 11 | beforeEach(() => { 12 | targetElement = document.createElement('div'); 13 | targetElement.setAttribute('class', 'foobar'); 14 | const textnode = document.createTextNode('Hello'); 15 | targetElement.appendChild(textnode); 16 | document.getElementsByTagName('body')[0].appendChild(targetElement); 17 | 18 | hostElement = document.createElement('div'); 19 | hostElement.setAttribute('class', 'hostelement'); 20 | const textnode2 = document.createTextNode('Im host'); 21 | hostElement.appendChild(textnode2); 22 | document.getElementsByTagName('body')[0].appendChild(hostElement); 23 | viewport.set(1000, 480); 24 | 25 | ww = window.innerWidth; 26 | wh = window.innerHeight; 27 | }); 28 | afterEach(() => { 29 | document.getElementsByTagName('body')[0].removeChild(targetElement); 30 | document.getElementsByTagName('body')[0].removeChild(hostElement); 31 | viewport.set(1000, 480); 32 | }); 33 | 34 | it('should have target element in document', () => { 35 | expect(document.querySelector('.foobar').textContent).toBe('Hello'); 36 | }); 37 | it('should get updated config', () => { 38 | const gloPos = new GlobalPosition({}); 39 | gloPos.updateConfig({ offset: 2 }); 40 | expect(gloPos['config']).toEqual({ 41 | placement: InsidePlacement.CENTER, 42 | width: 100, 43 | height: 100, 44 | offset: 2 45 | }); 46 | }); 47 | it('should return correct class name', () => { 48 | const gloPos = new GlobalPosition({}); 49 | expect(gloPos.getClassName()).toBe('global-position'); 50 | }); 51 | 52 | describe('#getPositions', () => { 53 | let srcCoords; 54 | beforeEach(() => { 55 | srcCoords = targetElement.getBoundingClientRect(); 56 | }); 57 | it('when exact width and height is provided in px', () => { 58 | const gloPos = new GlobalPosition({ 59 | width: 4, 60 | height: 10, 61 | placement: InsidePlacement.TOP 62 | }); 63 | expect(gloPos.getPositions(hostElement)).toEqual({ 64 | left: (ww - 4) / 2, 65 | top: 0, 66 | width: 4, 67 | height: 10, 68 | position: 'fixed', 69 | extra: InsidePlacement.TOP 70 | }); 71 | }); 72 | it('when exact width and height is provided in negative px', () => { 73 | const gloPos = new GlobalPosition({ 74 | width: -4, 75 | height: -10, 76 | placement: InsidePlacement.TOP 77 | }); 78 | expect(gloPos.getPositions(hostElement)).toEqual({ 79 | left: (ww - 4) / 2, 80 | top: 0, 81 | width: 4, 82 | height: 10, 83 | position: 'fixed', 84 | extra: InsidePlacement.TOP 85 | }); 86 | }); 87 | it('when exact width and height is provided in percentage', () => { 88 | const gloPos = new GlobalPosition({ 89 | width: '50%', 90 | height: '50%', 91 | placement: InsidePlacement.TOP 92 | }); 93 | expect(gloPos.getPositions(hostElement)).toEqual({ 94 | left: (ww - hostElement.offsetWidth) / 2, 95 | top: 0, 96 | width: `calc(${ww}px - 50%)`, 97 | height: `calc(${wh}px - 50%)`, 98 | position: 'fixed', 99 | extra: InsidePlacement.TOP 100 | }); 101 | }); 102 | it('when exact width and height is provided in higher percentage', () => { 103 | const gloPos = new GlobalPosition({ 104 | width: '150%', 105 | height: '150%', 106 | placement: InsidePlacement.TOP 107 | }); 108 | expect(gloPos.getPositions(hostElement)).toEqual({ 109 | left: (ww - hostElement.offsetWidth) / 2, 110 | top: 0, 111 | width: `calc(${ww}px - 0%)`, 112 | height: `calc(${wh}px - 0%)`, 113 | position: 'fixed', 114 | extra: InsidePlacement.TOP 115 | }); 116 | }); 117 | it('when no width and height is provided', () => { 118 | const gloPos = new GlobalPosition({ placement: InsidePlacement.TOP_RIGHT }); 119 | expect(gloPos.getPositions(hostElement)).toEqual({ 120 | right: 0, 121 | top: 0, 122 | width: 100, 123 | height: 100, 124 | position: 'fixed', 125 | extra: InsidePlacement.TOP_RIGHT 126 | }); 127 | }); 128 | }); 129 | describe('#calc', () => { 130 | const targetElCoords = { 131 | width: (window as any).innerWidth, 132 | height: (window as any).innerHeight 133 | }; 134 | 135 | const hostElCoords = { 136 | width: 4, // actual 967 137 | height: 10 // actual 18 138 | }; 139 | getData().forEach(data => { 140 | it(data.name, () => { 141 | const gloPos = new GlobalPosition({ 142 | width: hostElCoords.width, 143 | height: hostElCoords.height, 144 | placement: data.placement, 145 | offset: 2 146 | }); 147 | const pos = (gloPos as any).calc(data.placement, targetElCoords, hostElCoords); 148 | expect(pos).toEqual(data.expected); 149 | }); 150 | }); 151 | }); 152 | function getData() { 153 | // offset is 2 154 | ww = window.innerWidth; 155 | wh = window.innerHeight; 156 | const tests = [ 157 | { 158 | name: 'bottom', 159 | placement: InsidePlacement.BOTTOM, 160 | method: `calculate_${InsidePlacement.BOTTOM}`, 161 | expected: { left: (ww - 4) / 2, bottom: 2 } 162 | }, 163 | { 164 | name: 'top', 165 | placement: InsidePlacement.TOP, 166 | method: `calculate_${InsidePlacement.TOP}`, 167 | expected: { left: (ww - 4) / 2, top: 2 } 168 | }, 169 | { 170 | name: 'left', 171 | placement: InsidePlacement.LEFT, 172 | method: `calculate_${InsidePlacement.LEFT}`, 173 | expected: { left: 2, top: (wh - 10) / 2 } 174 | }, 175 | { 176 | name: 'right', 177 | placement: InsidePlacement.RIGHT, 178 | method: `calculate_${InsidePlacement.RIGHT}`, 179 | expected: { right: 2, top: (wh - 10) / 2 } 180 | }, 181 | { 182 | name: 'center', 183 | placement: InsidePlacement.CENTER, 184 | method: `calculate_${InsidePlacement.CENTER}`, 185 | expected: { left: (ww - 4) / 2, top: (wh - 10) / 2 } 186 | }, 187 | { 188 | name: 'top left', 189 | placement: InsidePlacement.TOP_LEFT, 190 | method: `calculate_${InsidePlacement.TOP_LEFT}`, 191 | expected: { left: 2, top: 2 } 192 | }, 193 | { 194 | name: 'top right', 195 | placement: InsidePlacement.TOP_RIGHT, 196 | method: `calculate_${InsidePlacement.TOP_RIGHT}`, 197 | expected: { right: 2, top: 2 } 198 | }, 199 | { 200 | name: 'bottom left', 201 | placement: InsidePlacement.BOTTOM_LEFT, 202 | method: `calculate_${InsidePlacement.BOTTOM_LEFT}`, 203 | expected: { left: 2, bottom: 2 } 204 | }, 205 | { 206 | name: 'bottom right', 207 | placement: InsidePlacement.BOTTOM_RIGHT, 208 | method: `calculate_${InsidePlacement.BOTTOM_RIGHT}`, 209 | expected: { right: 2, bottom: 2 } 210 | } 211 | ]; 212 | 213 | return tests; 214 | } 215 | }); 216 | -------------------------------------------------------------------------------- /projects/toppy/src/tests/positions/relative-position.spec.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { Subject } from 'rxjs'; 4 | import { take } from 'rxjs/operators'; 5 | import { Bus } from 'toppy/lib/utils'; 6 | import { OutsidePlacement } from '../../lib/models'; 7 | import { RelativePosition } from '../../lib/position'; 8 | 9 | describe('@ RelativePosition', () => { 10 | let targetElement: HTMLElement; 11 | let hostElement: HTMLElement; 12 | let die: Subject; 13 | beforeEach(() => { 14 | targetElement = document.createElement('div'); 15 | targetElement.setAttribute('class', 'foobar'); 16 | const textnode = document.createTextNode('Hello'); 17 | targetElement.appendChild(textnode); 18 | document.getElementsByTagName('body')[0].appendChild(targetElement); 19 | 20 | hostElement = document.createElement('div'); 21 | hostElement.setAttribute('class', 'hostelement'); 22 | const textnode2 = document.createTextNode('Im host'); 23 | hostElement.appendChild(textnode2); 24 | document.getElementsByTagName('body')[0].appendChild(hostElement); 25 | viewport.set(1000, 480); 26 | }); 27 | afterEach(() => { 28 | document.getElementsByTagName('body')[0].removeChild(targetElement); 29 | document.getElementsByTagName('body')[0].removeChild(hostElement); 30 | viewport.set(1000, 480); 31 | }); 32 | 33 | beforeEach(() => { 34 | die = new Subject(); 35 | Bus['_e'] = new Subject(); 36 | }); 37 | 38 | afterEach(function() { 39 | die.next(true); 40 | die.complete(); 41 | Bus.stop(); 42 | }); 43 | 44 | it('should have target element in document', () => { 45 | expect(document.querySelector('.foobar').textContent).toBe('Hello'); 46 | }); 47 | it('should get updated config', () => { 48 | const relPos = new RelativePosition({ src: null }); 49 | relPos.updateConfig({ autoUpdate: true }); 50 | expect(relPos['config']).toEqual({ 51 | src: null, 52 | placement: OutsidePlacement.TOP, 53 | autoUpdate: true, 54 | width: 'auto', 55 | height: 'auto' 56 | }); 57 | }); 58 | it('should return correct class name', () => { 59 | const relPos = new RelativePosition({ src: null }); 60 | expect(relPos.getClassName()).toBe('relative-position'); 61 | }); 62 | 63 | describe('#autoUpdate', () => { 64 | it('should switch if it is true - case 1', () => { 65 | const relPos = new RelativePosition({ 66 | src: targetElement, 67 | placement: OutsidePlacement.TOP, 68 | height: 500, 69 | autoUpdate: true 70 | }); 71 | const srcCoords = targetElement.getBoundingClientRect(); 72 | const hostElCoords = { 73 | width: 4, // actual 967 74 | height: 450 75 | }; 76 | expect((relPos as any).calculatePos(OutsidePlacement.TOP, srcCoords, hostElCoords, true)).toEqual({ 77 | props: { 78 | left: 8 + (srcCoords.width - 4) / 2, 79 | top: srcCoords.top + srcCoords.height 80 | }, 81 | pos: OutsidePlacement.BOTTOM 82 | }); 83 | }); 84 | it('should switch if it is true - case 2', () => { 85 | const relPos = new RelativePosition({ 86 | src: targetElement, 87 | placement: OutsidePlacement.TOP_LEFT, 88 | height: 500, 89 | autoUpdate: true 90 | }); 91 | const srcCoords = targetElement.getBoundingClientRect(); 92 | const hostElCoords = { 93 | width: 4, // actual 967 94 | height: 450 95 | }; 96 | expect((relPos as any).calculatePos(OutsidePlacement.TOP_LEFT, srcCoords, hostElCoords, true)).toEqual({ 97 | props: { 98 | left: 8, 99 | top: srcCoords.top + srcCoords.height 100 | }, 101 | pos: OutsidePlacement.BOTTOM_LEFT 102 | }); 103 | }); 104 | it('should not switch if it is false', () => { 105 | const relPos = new RelativePosition({ 106 | src: targetElement, 107 | placement: OutsidePlacement.TOP, 108 | height: 500, 109 | autoUpdate: false 110 | }); 111 | const srcCoords = targetElement.getBoundingClientRect(); 112 | const hostElCoords = { 113 | width: 4, // actual 967 114 | height: 150 115 | }; 116 | expect((relPos as any).calculatePos(OutsidePlacement.TOP, srcCoords, hostElCoords, true)).toEqual({ 117 | props: { 118 | left: 8 + (srcCoords.width - 4) / 2, 119 | top: srcCoords.top - 150 120 | }, 121 | pos: OutsidePlacement.TOP 122 | }); 123 | }); 124 | }); 125 | describe('#getPositions', () => { 126 | let srcCoords; 127 | beforeEach(() => { 128 | srcCoords = targetElement.getBoundingClientRect(); 129 | }); 130 | it('when exact width and height is provided', () => { 131 | const relPos = new RelativePosition({ 132 | width: 4, 133 | height: 10, 134 | src: targetElement, 135 | placement: OutsidePlacement.TOP 136 | }); 137 | expect(relPos.getPositions(hostElement)).toEqual({ 138 | left: Math.round(8 + (targetElement.offsetWidth - 4) / 2), 139 | top: Math.round(srcCoords.top - 10), 140 | width: 4, 141 | height: 10, 142 | extra: OutsidePlacement.TOP 143 | }); 144 | }); 145 | it('when no width and height is provided', () => { 146 | const relPos = new RelativePosition({ src: targetElement, placement: OutsidePlacement.TOP }); 147 | expect(relPos.getPositions(hostElement)).toEqual({ 148 | left: Math.round(8 + (967 - 967) / 2), 149 | top: Math.round(srcCoords.top - 18), 150 | width: 'auto', 151 | height: 'auto', 152 | extra: OutsidePlacement.TOP 153 | }); 154 | }); 155 | }); 156 | describe('#listenDrag', () => { 157 | let relPos; 158 | beforeEach(() => { 159 | relPos = new RelativePosition({ src: targetElement, autoUpdate: true }); 160 | relPos.init('abc'); 161 | }); 162 | 163 | it('should emit proper event when drag', done => { 164 | Bus.listen('abc', 't_dynpos') 165 | .pipe(take(1)) 166 | .subscribe(res => { 167 | expect(res).toEqual(null); 168 | done(); 169 | }); 170 | targetElement.style.left = '0px'; 171 | }); 172 | }); 173 | describe('#calc', () => { 174 | const targetElCoords = { 175 | bottom: 76, 176 | height: 18, 177 | left: 8, 178 | right: 975, 179 | top: 58, 180 | width: 967 181 | }; 182 | 183 | const hostElCoords = { 184 | width: 4, // actual 967 185 | height: 10 // actual 18 186 | }; 187 | getData().forEach(data => { 188 | it(data.name, () => { 189 | const relPos = new RelativePosition({ 190 | src: targetElement, 191 | width: hostElCoords.width, 192 | height: hostElCoords.height, 193 | placement: data.placement 194 | }); 195 | const pos = (relPos as any).calc(data.placement, targetElCoords, hostElCoords); 196 | expect(pos).toEqual(data.expected); 197 | }); 198 | }); 199 | }); 200 | }); 201 | 202 | function getData() { 203 | const tests = [ 204 | { 205 | name: 'bottom', 206 | placement: OutsidePlacement.BOTTOM, 207 | method: `calculate_${OutsidePlacement.BOTTOM}`, 208 | expected: { left: 8 + (967 - 4) / 2, top: 76 } 209 | }, 210 | { 211 | name: 'top', 212 | placement: OutsidePlacement.TOP, 213 | method: `calculate_${OutsidePlacement.TOP}`, 214 | expected: { left: 8 + (967 - 4) / 2, top: 48 } 215 | }, 216 | { 217 | name: 'left', 218 | placement: OutsidePlacement.LEFT, 219 | method: `calculate_${OutsidePlacement.LEFT}`, 220 | expected: { left: 8 - 4, top: 58 + (18 - 10) / 2 } 221 | }, 222 | { 223 | name: 'right', 224 | placement: OutsidePlacement.RIGHT, 225 | method: `calculate_${OutsidePlacement.RIGHT}`, 226 | expected: { left: 975, top: 58 + (18 - 10) / 2 } 227 | }, 228 | { 229 | name: 'top left', 230 | placement: OutsidePlacement.TOP_LEFT, 231 | method: `calculate_${OutsidePlacement.TOP_LEFT}`, 232 | expected: { left: 8, top: 58 - 10 } 233 | }, 234 | { 235 | name: 'top right', 236 | placement: OutsidePlacement.TOP_RIGHT, 237 | method: `calculate_${OutsidePlacement.TOP_RIGHT}`, 238 | expected: { left: 8 + (967 - 4), top: 58 - 10 } 239 | }, 240 | { 241 | name: 'bottom left', 242 | placement: OutsidePlacement.BOTTOM_LEFT, 243 | method: `calculate_${OutsidePlacement.BOTTOM_LEFT}`, 244 | expected: { left: 8, top: 76 } 245 | }, 246 | { 247 | name: 'bottom right', 248 | placement: OutsidePlacement.BOTTOM_RIGHT, 249 | method: `calculate_${OutsidePlacement.BOTTOM_RIGHT}`, 250 | expected: { left: 8 + (967 - 4), top: 76 } 251 | }, 252 | { 253 | name: 'right top', 254 | placement: OutsidePlacement.RIGHT_TOP, 255 | method: `calculate_${OutsidePlacement.RIGHT_TOP}`, 256 | expected: { left: 975, top: 58 } 257 | }, 258 | { 259 | name: 'right bottom', 260 | placement: OutsidePlacement.RIGHT_BOTTOM, 261 | method: `calculate_${OutsidePlacement.RIGHT_BOTTOM}`, 262 | expected: { left: 975, top: 76 - 10 } 263 | }, 264 | { 265 | name: 'left top', 266 | placement: OutsidePlacement.LEFT_TOP, 267 | method: `calculate_${OutsidePlacement.LEFT_TOP}`, 268 | expected: { left: 8 - 4, top: 58 } 269 | }, 270 | { 271 | name: 'left bottom', 272 | placement: OutsidePlacement.LEFT_BOTTOM, 273 | method: `calculate_${OutsidePlacement.LEFT_BOTTOM}`, 274 | expected: { left: 8 - 4, top: 76 - 10 } 275 | } 276 | ]; 277 | 278 | return tests; 279 | } 280 | -------------------------------------------------------------------------------- /projects/toppy/src/tests/toppy.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApplicationRef, 3 | Component, 4 | ComponentFactoryResolver, 5 | ElementRef, 6 | Injector, 7 | NgModule, 8 | TemplateRef, 9 | ViewChild 10 | } from '@angular/core'; 11 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 12 | import { Subject } from 'rxjs'; 13 | import { DefaultConfig } from '../lib/config'; 14 | import { ContentType } from '../lib/models'; 15 | import { GlobalPosition, RelativePosition } from '../lib/position'; 16 | import { Toppy } from '../lib/toppy'; 17 | import { ToppyControl } from '../lib/toppy-control'; 18 | import { Bus } from '../lib/utils'; 19 | 20 | @Component({ 21 | selector: 'lib-template-ref-test-comp', 22 | template: ` 23 | some content 24 | I am template 25 | ` 26 | }) 27 | export class TemplateRefTestComponent { 28 | @ViewChild('el', { read: ElementRef }) el: ElementRef; 29 | @ViewChild('tpl', { read: TemplateRef }) tpl; 30 | constructor(private toppy: Toppy) {} 31 | } 32 | 33 | @Component({ 34 | template: 'Hi' 35 | }) 36 | export class TestComponent {} 37 | 38 | @NgModule({ 39 | declarations: [TestComponent, TemplateRefTestComponent], 40 | entryComponents: [TestComponent] 41 | }) 42 | export class TemplateRefTestModule {} 43 | 44 | describe('@ Toppy', () => { 45 | let toppy: Toppy = null; 46 | let templateRefCompFixture: ComponentFixture; 47 | let templateRefComp: TemplateRefTestComponent; 48 | let appRef, compFact, inj; 49 | const config = DefaultConfig; 50 | 51 | beforeEach(async(() => { 52 | TestBed.configureTestingModule({ 53 | imports: [TemplateRefTestModule], 54 | declarations: [], 55 | providers: [ApplicationRef, ComponentFactoryResolver, Injector] 56 | }).compileComponents(); 57 | 58 | templateRefCompFixture = TestBed.createComponent(TemplateRefTestComponent); 59 | templateRefComp = templateRefCompFixture.componentInstance; 60 | toppy = TestBed.get(Toppy); 61 | appRef = TestBed.get(ApplicationRef); 62 | compFact = TestBed.get(ComponentFactoryResolver); 63 | inj = TestBed.get(Injector); 64 | spyOn(toppy, 'destroy').and.callThrough(); 65 | })); 66 | 67 | afterEach(function() { 68 | templateRefCompFixture.destroy(); 69 | document.querySelector('body').removeChild(templateRefCompFixture.debugElement.nativeElement); 70 | }); 71 | afterAll(() => { 72 | Bus['_e'] = new Subject(); 73 | }); 74 | 75 | it('should be initialized', () => { 76 | expect(toppy).toBeTruthy(); 77 | }); 78 | 79 | describe('#basic', () => { 80 | let ctrl: ToppyControl; 81 | beforeEach(() => { 82 | ctrl = toppy.create(); 83 | }); 84 | afterEach(() => { 85 | toppy.destroy(); 86 | }); 87 | it('should return "ToppyControl"', () => { 88 | expect(ctrl instanceof ToppyControl).toBeTruthy(); 89 | }); 90 | it('should have default "GlobalPosition"', () => { 91 | expect(ctrl.position instanceof GlobalPosition).toBeTruthy(); 92 | }); 93 | it('should have default config', () => { 94 | expect(ctrl.config).toEqual(DefaultConfig); 95 | }); 96 | it('should have default text content', () => { 97 | expect(ctrl.content).toEqual({ data: 'hello', type: ContentType.STRING, props: {} }); 98 | }); 99 | it('should add control to toppy', () => { 100 | expect(Object.keys(Toppy.controls).length).toEqual(1); 101 | }); 102 | }); 103 | describe('#config|#position|#content', () => { 104 | let ctrl: ToppyControl; 105 | let tid: string; 106 | beforeEach(() => { 107 | ctrl = toppy 108 | .position(new RelativePosition({ src: templateRefComp.el.nativeElement })) 109 | .config({ backdropClass: 't-custom-backdrop' }) 110 | .content('random text') 111 | .create(); 112 | tid = toppy['tid']; 113 | }); 114 | afterEach(() => { 115 | toppy.destroy(); 116 | }); 117 | it('should set custom config in "Toppy"', () => { 118 | expect((toppy as any).inputs.config).toEqual({ ...DefaultConfig, backdropClass: 't-custom-backdrop' }); 119 | }); 120 | it('should set custom config in "ToppyControl"', () => { 121 | expect(toppy.getCtrl(tid).config).toEqual({ ...DefaultConfig, backdropClass: 't-custom-backdrop' }); 122 | }); 123 | it('should set custom position in "Toppy"', () => { 124 | expect((toppy as any).inputs.position instanceof RelativePosition).toBeTruthy(); 125 | }); 126 | it('should set custom position in "ToppyControl"', () => { 127 | expect(toppy.getCtrl(tid).position instanceof RelativePosition).toBeTruthy(); 128 | }); 129 | it('should set custom content in "Toppy"', () => { 130 | expect((toppy as any).inputs.content).toEqual({ 131 | type: ContentType.STRING, 132 | data: 'random text', 133 | props: {} 134 | }); 135 | }); 136 | it('should set custom content in "ToppyControl"', () => { 137 | expect(toppy.getCtrl(tid).content).toEqual({ type: ContentType.STRING, data: 'random text', props: {} }); 138 | }); 139 | }); 140 | describe('#content', () => { 141 | let t: Toppy; 142 | let tid: string; 143 | beforeEach(() => { 144 | t = toppy 145 | .position(new RelativePosition({ src: templateRefComp.el.nativeElement })) 146 | .config({ backdropClass: 't-custom-backdrop' }); 147 | }); 148 | afterEach(() => { 149 | toppy.destroy(); 150 | }); 151 | describe('should return content type as STRING', () => { 152 | it('when without props', () => { 153 | t.content('hello').create(); 154 | tid = toppy['tid']; 155 | expect(toppy.getCtrl(tid).content).toEqual({ type: ContentType.STRING, data: 'hello', props: {} }); 156 | }); 157 | it('when with props', () => { 158 | t.content('hello', { class: 'abc' }).create(); 159 | tid = toppy['tid']; 160 | expect(toppy.getCtrl(tid).content).toEqual({ 161 | type: ContentType.STRING, 162 | data: 'hello', 163 | props: { class: 'abc' } 164 | }); 165 | }); 166 | }); 167 | describe('should return content type as HTML', () => { 168 | it('when without props', () => { 169 | t.content('hello').create(); 170 | tid = toppy['tid']; 171 | expect(toppy.getCtrl(tid).content).toEqual({ 172 | type: ContentType.STRING, 173 | data: 'hello', 174 | props: {} 175 | }); 176 | }); 177 | it('when with props', () => { 178 | t.content('hello', { hasHTML: true }).create(); 179 | tid = toppy['tid']; 180 | expect(toppy.getCtrl(tid).content).toEqual({ 181 | type: ContentType.HTML, 182 | data: 'hello', 183 | props: { hasHTML: true } 184 | }); 185 | }); 186 | }); 187 | describe('should return content type as TEMPLATE', () => { 188 | it('when without props', () => { 189 | t.content(templateRefComp.tpl).create(); 190 | tid = toppy['tid']; 191 | expect(toppy.getCtrl(tid).content).toEqual({ 192 | type: ContentType.TEMPLATE, 193 | data: templateRefComp.tpl, 194 | props: {} 195 | }); 196 | }); 197 | it('when with props', () => { 198 | t.content(templateRefComp.tpl, { name: 'Johny' }).create(); 199 | tid = toppy['tid']; 200 | expect(toppy.getCtrl(tid).content).toEqual({ 201 | type: ContentType.TEMPLATE, 202 | data: templateRefComp.tpl, 203 | props: { name: 'Johny' } 204 | }); 205 | }); 206 | }); 207 | describe('should return content type as COMPONENT', () => { 208 | it('when without props', () => { 209 | t.content(TestComponent).create(); 210 | tid = toppy['tid']; 211 | expect(toppy.getCtrl(tid).content).toEqual({ 212 | type: ContentType.COMPONENT, 213 | data: TestComponent, 214 | props: {} 215 | }); 216 | }); 217 | it('when with props', () => { 218 | t.content(TestComponent, { name: 'Johny' }).create(); 219 | tid = toppy['tid']; 220 | expect(toppy.getCtrl(tid).content).toEqual({ 221 | type: ContentType.COMPONENT, 222 | data: TestComponent, 223 | props: { name: 'Johny' } 224 | }); 225 | }); 226 | }); 227 | }); 228 | describe('#create', () => { 229 | let ctrl1: ToppyControl; 230 | let t: Toppy; 231 | let firstTid, secondTid; 232 | beforeEach(() => { 233 | ctrl1 = toppy.create(); 234 | firstTid = toppy['tid']; 235 | 236 | t = toppy.content('abc'); 237 | (t as any).tid = firstTid; 238 | t.create(); 239 | secondTid = toppy['tid']; 240 | }); 241 | afterEach(() => { 242 | toppy.destroy(); 243 | }); 244 | it('should create new instance if the tid already exists', () => { 245 | expect(Object.keys(Toppy.controls)).toEqual([firstTid, secondTid]); 246 | }); 247 | it('should add multiple new instances', () => { 248 | toppy.create(); 249 | toppy.create(); 250 | toppy.create(); 251 | expect(Object.keys(Toppy.controls).length).toEqual(5); 252 | }); 253 | it('should add new instance with custom key', () => { 254 | toppy.create('JKL'); 255 | expect(Object.keys(Toppy.controls)).toContain('JKL'); 256 | }); 257 | it('should create new key if same key already exists', () => { 258 | toppy.create('ABCD'); 259 | toppy.create('ABCD'); 260 | expect(Object.keys(Toppy.controls).length).toEqual(4); 261 | expect(Object.keys(Toppy.controls).filter(x => x === 'ABCD').length).toEqual(1); 262 | }); 263 | }); 264 | describe('#destroy', () => { 265 | it('should remove all controls', () => { 266 | toppy.create(); 267 | toppy.create(); 268 | toppy.create(); 269 | toppy.create(); 270 | 271 | toppy.destroy(); 272 | expect(Object.keys(Toppy.controls).length).toEqual(0); 273 | }); 274 | }); 275 | }); 276 | -------------------------------------------------------------------------------- /projects/toppy/src/tests/toppy.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { Component, DebugElement, EventEmitter, Input, NgModule, Output } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { Subject } from 'rxjs'; 5 | import { skip, switchMap, take } from 'rxjs/operators'; 6 | import { DefaultConfig } from '../lib/config'; 7 | import { ContentType } from '../lib/models'; 8 | import { ToppyComponent } from '../lib/toppy.component'; 9 | import { Bus } from '../lib/utils'; 10 | 11 | @Component({ 12 | selector: 'test-comp', 13 | template: 'Hello {{name||"John"}}' 14 | }) 15 | export class TestComponent { 16 | @Input() data = null; 17 | @Output() fire: EventEmitter = new EventEmitter(); 18 | name; 19 | } 20 | 21 | @NgModule({ 22 | declarations: [TestComponent], 23 | entryComponents: [TestComponent], 24 | exports: [TestComponent] 25 | }) 26 | export class TestModule {} 27 | 28 | describe('@ ToppyComponent', () => { 29 | let fixture: ComponentFixture; 30 | let debugEl: DebugElement; 31 | let toppyComp: ToppyComponent; 32 | let el: HTMLElement; 33 | let die: Subject; 34 | beforeEach(async(() => { 35 | TestBed.configureTestingModule({ 36 | imports: [TestModule], 37 | declarations: [ToppyComponent] 38 | }).compileComponents(); 39 | fixture = TestBed.createComponent(ToppyComponent); 40 | debugEl = fixture.debugElement; 41 | el = debugEl.nativeElement; 42 | toppyComp = fixture.componentInstance; 43 | })); 44 | 45 | beforeEach(() => { 46 | toppyComp.tid = 'abc'; 47 | toppyComp.config = { ...DefaultConfig, bodyClass: 'zzz' }; 48 | toppyComp.position = { 49 | getClassName: () => 'relative', 50 | getPositions: c => ({ left: 45, top: 79, extra: 't' }) 51 | } as any; 52 | die = new Subject(); 53 | Bus['_e'] = new Subject(); 54 | }); 55 | 56 | afterEach(() => { 57 | fixture.destroy(); 58 | die.next(true); 59 | die.complete(); 60 | Bus.stop(); 61 | }); 62 | 63 | it('should be initialized', () => { 64 | expect(toppyComp).toBeTruthy(); 65 | }); 66 | describe('#ngOnInit', () => { 67 | it('should add data-tid attribute', () => { 68 | fixture.detectChanges(); 69 | expect(el.getAttribute('data-tid')).toEqual('abc'); 70 | }); 71 | it('should add default classes', () => { 72 | fixture.detectChanges(); 73 | expect(el.classList.value).toBe('t-container t-overlay relative'); 74 | }); 75 | it('should add class for docClick', () => { 76 | toppyComp.config.closeOnDocClick = true; 77 | fixture.detectChanges(); 78 | expect(el.classList.value).toBe('t-container t-overlay relative no-pointers'); 79 | }); 80 | it('should add class for bodyClass', () => { 81 | fixture.detectChanges(); 82 | expect(document.querySelector('body').classList.value).toEqual('zzz'); 83 | }); 84 | }); 85 | 86 | describe('#ngAfterViewInit', () => { 87 | it('should call setPos', () => { 88 | fixture.detectChanges(); 89 | spyOn(toppyComp as any, 'setPos'); 90 | toppyComp.ngAfterViewInit(); 91 | expect(toppyComp['setPos']).toHaveBeenCalled(); 92 | }); 93 | it('should subscribe to triggerPosChange', () => { 94 | spyOn(toppyComp as any, 'setPos'); 95 | fixture.detectChanges(); 96 | toppyComp.ngAfterViewInit(); 97 | expect(toppyComp['setPos']).toHaveBeenCalled(); 98 | }); 99 | it('should subscribe to listenPos', () => { 100 | fixture.detectChanges(); 101 | spyOn(toppyComp as any, 'setPos'); 102 | Bus.send('abc', 't_dynpos', null); 103 | toppyComp.ngAfterViewInit(); 104 | expect(toppyComp['setPos']).toHaveBeenCalled(); 105 | }); 106 | it('should subscribe to listenPos when custom data is arraived', () => { 107 | fixture.detectChanges(); 108 | toppyComp.ngAfterViewInit(); 109 | Bus.send('abc', 't_dynpos', { x: 10, y: 12 }); 110 | expect(el.querySelector('.t-wrapper').getAttribute('style')).toEqual('left: 10px; top: 12px;'); 111 | }); 112 | it('should render custom props in the template', () => { 113 | toppyComp.content.data = TestComponent; 114 | toppyComp.content.type = ContentType.COMPONENT; 115 | toppyComp.content.props = { 116 | name: 'Peter', 117 | id: 22 118 | }; 119 | fixture.detectChanges(); 120 | expect(fixture.debugElement.query(By.css('test-comp')).nativeElement.textContent).toBe('Hello Peter'); 121 | }); 122 | it('should emit host component instance', done => { 123 | toppyComp.content.data = TestComponent; 124 | toppyComp.content.type = ContentType.COMPONENT; 125 | toppyComp.content.props = { 126 | name: 'Loky' 127 | }; 128 | fixture.detectChanges(); 129 | Bus.listen('abc', 't_compins').subscribe(data => { 130 | expect(data.name).toEqual('Loky'); 131 | done(); 132 | }); 133 | toppyComp.ngAfterViewInit(); 134 | }); 135 | it('should able to access "@Output()"', done => { 136 | toppyComp.content.data = TestComponent; 137 | toppyComp.content.type = ContentType.COMPONENT; 138 | toppyComp.content.props = { name: 'SSSS' }; 139 | fixture.detectChanges(); 140 | Bus.listen('abc', 't_compins') 141 | .pipe(switchMap(a => a['fire'])) 142 | .subscribe(data => { 143 | expect(data).toEqual('ABCXYZ'); 144 | done(); 145 | }); 146 | toppyComp.ngAfterViewInit(); 147 | toppyComp.compInstance.fire.emit('ABCXYZ'); 148 | }); 149 | }); 150 | 151 | describe('#updateTextContent', () => { 152 | it('should not change the text if the content is not string', () => { 153 | toppyComp.content = { 154 | data: 'DUMMY', 155 | props: {}, 156 | type: ContentType.HTML 157 | }; 158 | const text = 'Little cute Dog'; 159 | toppyComp.updateTextContent(text); 160 | fixture.detectChanges(); 161 | expect(debugEl.query(By.css('.t-wrapper div')).nativeElement.textContent).toEqual('DUMMY'); 162 | }); 163 | it('should change the text content in template', () => { 164 | const text = 'Little cute Dog'; 165 | toppyComp.updateTextContent(text); 166 | fixture.detectChanges(); 167 | expect(debugEl.query(By.css('.t-wrapper div')).nativeElement.textContent).toEqual(text); 168 | }); 169 | }); 170 | 171 | describe('#ngOnDestroy', () => { 172 | it('should remove class name from body', () => { 173 | toppyComp.config.bodyClass = 'Bunny'; 174 | fixture.detectChanges(); 175 | expect(document.querySelector('body').classList.value).toEqual('Bunny'); 176 | fixture.destroy(); 177 | expect(document.querySelector('body').classList.value).toEqual(''); 178 | }); 179 | it('should unsubscribe all events', () => { 180 | spyOn(toppyComp as any, 'setPos'); 181 | toppyComp.ngAfterViewInit(); 182 | Bus.send('abc', 't_dynpos'); 183 | expect(toppyComp['setPos']).toHaveBeenCalledTimes(2); 184 | fixture.destroy(); 185 | Bus.send('abc', 't_dynpos'); 186 | expect(toppyComp['setPos']).toHaveBeenCalledTimes(2); 187 | }); 188 | it('should fire t_detach event', () => { 189 | const spy = jasmine.createSpy().and.callThrough(); 190 | Bus.listen('abc', 't_detach') 191 | .pipe(take(1)) 192 | .subscribe(() => { 193 | spy(); 194 | spy(); 195 | }); 196 | fixture.destroy(); 197 | expect(spy.calls.count()).toEqual(2); 198 | }); 199 | }); 200 | 201 | describe('#listenPos', () => { 202 | it('should return new position', done => { 203 | fixture.detectChanges(); 204 | toppyComp['listenPos']().subscribe(data => { 205 | expect(data).toEqual(1); 206 | done(); 207 | }); 208 | }); 209 | it('should add styles', done => { 210 | fixture.detectChanges(); 211 | toppyComp['listenPos']() 212 | .pipe(take(1)) 213 | .subscribe(() => { 214 | expect(el.querySelector('.t-wrapper').getAttribute('style')).toEqual( 215 | 'left: 45px; top: 79px; visibility: visible; opacity: 1;' 216 | ); 217 | done(); 218 | }); 219 | }); 220 | it('should add styles', done => { 221 | fixture.detectChanges(); 222 | toppyComp['listenPos']() 223 | .pipe(skip(1)) 224 | .subscribe(() => { 225 | expect(el.querySelector('.t-wrapper').getAttribute('style')).toEqual('left: 99px; top: 22px;'); 226 | done(); 227 | }); 228 | Bus.send('abc', 't_dynpos', { x: 99, y: 22 }); 229 | }); 230 | }); 231 | 232 | describe('#setPos', () => { 233 | it('should set positions to wrapper element', () => { 234 | fixture.detectChanges(); 235 | toppyComp['setPos'](); 236 | expect(el.querySelector('.t-wrapper').getAttribute('style')).toEqual( 237 | 'left: 45px; top: 79px; visibility: visible; opacity: 1;' 238 | ); 239 | }); 240 | it('should emit position change event', () => { 241 | const spy = jasmine.createSpy().and.callThrough(); 242 | fixture.detectChanges(); 243 | Bus.listen('abc', 't_posupdate') 244 | .pipe(take(1)) 245 | .subscribe(() => { 246 | spy(); 247 | spy(); 248 | spy(); 249 | }); 250 | toppyComp['setPos'](); 251 | expect(spy.calls.count()).toEqual(3); 252 | }); 253 | it('should set extra class to wrapper element', () => { 254 | fixture.detectChanges(); 255 | toppyComp['setPos'](); 256 | expect(el.querySelector('.t-wrapper').classList.value).toEqual('t-wrapper t'); 257 | }); 258 | }); 259 | 260 | describe('#config', () => { 261 | it('should set custom class names', () => { 262 | toppyComp.config.backdrop = true; 263 | toppyComp.config.wrapperClass = 'custom-wrapper-class'; 264 | toppyComp.config.backdropClass = 'custom-backdrop-class'; 265 | fixture.detectChanges(); 266 | expect(el.querySelector('.t-wrapper').classList.value).toEqual('t-wrapper custom-wrapper-class t'); 267 | expect(el.querySelector('.t-backdrop').classList.value).toEqual('t-backdrop custom-backdrop-class'); 268 | }); 269 | }); 270 | }); 271 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.3.4](https://github.com/lokesh-coder/toppy/compare/v2.3.3...v2.3.4) (2019-07-31) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * backdrop custom class not added ([#35](https://github.com/lokesh-coder/toppy/issues/35)) ([23752b9](https://github.com/lokesh-coder/toppy/commit/23752b9)) 7 | 8 | ## [2.3.3](https://github.com/lokesh-coder/toppy/compare/v2.3.2...v2.3.3) (2019-03-13) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * relative positon incorrect placement on `autoUpdate` ([f3c0106](https://github.com/lokesh-coder/toppy/commit/f3c0106)) 14 | 15 | ## [2.3.2](https://github.com/lokesh-coder/toppy/compare/v2.3.1...v2.3.2) (2019-02-12) 16 | 17 | ## [2.3.1](https://github.com/lokesh-coder/toppy/compare/v2.3.0...v2.3.1) (2019-02-10) 18 | 19 | # [2.3.0](https://github.com/lokesh-coder/toppy/compare/v2.2.0...v2.3.0) (2019-01-05) 20 | 21 | 22 | ### Features 23 | 24 | * add support to access host component instance ([6cf180a](https://github.com/lokesh-coder/toppy/commit/6cf180a)) 25 | 26 | # [2.2.0](https://github.com/lokesh-coder/toppy/compare/v2.1.0...v2.2.0) (2018-12-31) 27 | 28 | 29 | ### Features 30 | 31 | * attach component custom props on initialize without ToppyOverlay ([e96a5f4](https://github.com/lokesh-coder/toppy/commit/e96a5f4)) 32 | 33 | # [2.1.0](https://github.com/lokesh-coder/toppy/compare/v2.0.5...v2.1.0) (2018-12-25) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * re-render when nested dynamic component ([68f5844](https://github.com/lokesh-coder/toppy/commit/68f5844)) 39 | 40 | 41 | ### Features 42 | 43 | * add position classes to warpper element ([79fc1a5](https://github.com/lokesh-coder/toppy/commit/79fc1a5)) 44 | * add position classes to warpper element ([b8ffb44](https://github.com/lokesh-coder/toppy/commit/b8ffb44)) 45 | 46 | ## [2.0.5](https://github.com/lokesh-coder/toppy/compare/v2.0.4...v2.0.5) (2018-12-24) 47 | 48 | ## [2.0.4](https://github.com/lokesh-coder/toppy/compare/v2.0.3...v2.0.4) (2018-12-20) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * allow custom props to be accessed in host component ([9d8e4ba](https://github.com/lokesh-coder/toppy/commit/9d8e4ba)) 54 | 55 | ## [2.0.3](https://github.com/lokesh-coder/toppy/compare/v2.0.2...v2.0.3) (2018-12-15) 56 | 57 | ## [2.0.2](https://github.com/lokesh-coder/toppy/compare/v2.0.1...v2.0.2) (2018-12-15) 58 | 59 | 60 | ### Bug Fixes 61 | 62 | * incorrect signature in API ([c80658e](https://github.com/lokesh-coder/toppy/commit/c80658e)) 63 | 64 | ## [2.0.1](https://github.com/lokesh-coder/toppy/compare/v2.0.0...v2.0.1) (2018-12-14) 65 | 66 | # [2.0.0](https://github.com/lokesh-coder/toppy/compare/v1.3.1...v2.0.0) (2018-12-14) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * remove unused components ([10a3fd8](https://github.com/lokesh-coder/toppy/commit/10a3fd8)) 72 | * ss class method [skip ci] ([521c2d1](https://github.com/lokesh-coder/toppy/commit/521c2d1)) 73 | 74 | 75 | ### Features 76 | 77 | * access custom props in Component, Template and Plain text contents ([b8550c8](https://github.com/lokesh-coder/toppy/commit/b8550c8)) 78 | * add template ref context ([e3e4ff1](https://github.com/lokesh-coder/toppy/commit/e3e4ff1)), closes [#19](https://github.com/lokesh-coder/toppy/issues/19) 79 | * added support for custom key to reference later ([3901878](https://github.com/lokesh-coder/toppy/commit/3901878)) 80 | * include context data in templateref ([a865929](https://github.com/lokesh-coder/toppy/commit/a865929)) 81 | 82 | 83 | ### BREAKING CHANGES 84 | 85 | * API has been changed. `.overlay()` to `.position()` , `.host()` to `.content()` , `updateHost()` to `updateContent()` , `ToppyRef` to `ToppyControl`. Introduced `.config()`. Performance improvement. 86 | 87 | ## [1.3.1](https://github.com/lokesh-coder/toppy/compare/v1.3.0...v1.3.1) (2018-12-05) 88 | 89 | 90 | ### Bug Fixes 91 | 92 | * **global position:** calculate proper left and top co-ordinates when `hostWidth` and `hostHeight` in percentage ([59dd68b](https://github.com/lokesh-coder/toppy/commit/59dd68b)) 93 | 94 | # [1.3.0](https://github.com/lokesh-coder/toppy/compare/v1.2.4...v1.3.0) (2018-12-04) 95 | 96 | 97 | ### Features 98 | 99 | * **relative position:** added support for content sticking with target element when `autoUpdate` is set to true ([6fa37e7](https://github.com/lokesh-coder/toppy/commit/6fa37e7)) 100 | 101 | ## [1.2.4](https://github.com/lokesh-coder/toppy/compare/v1.2.3...v1.2.4) (2018-11-28) 102 | 103 | 104 | ### Bug Fixes 105 | 106 | * replace codeclimate with codecov config in travis ([af9d16f](https://github.com/lokesh-coder/toppy/commit/af9d16f)) 107 | 108 | ## [1.2.3](https://github.com/lokesh-coder/toppy/compare/v1.2.2...v1.2.3) (2018-11-28) 109 | 110 | 111 | ### Bug Fixes 112 | 113 | * **docs:** update font path ([29834a5](https://github.com/lokesh-coder/toppy/commit/29834a5)) 114 | * move artifacts after build ([d7ff131](https://github.com/lokesh-coder/toppy/commit/d7ff131)) 115 | 116 | ## [1.2.2](https://github.com/lokesh-coder/toppy/compare/v1.2.1...v1.2.2) (2018-11-28) 117 | 118 | 119 | ### Bug Fixes 120 | 121 | * updated vulnerable dependencies ([ace393d](https://github.com/lokesh-coder/toppy/commit/ace393d)) 122 | * **docs:** update icon fonts path ([eedca4c](https://github.com/lokesh-coder/toppy/commit/eedca4c)) 123 | 124 | ## [1.2.1](https://github.com/lokesh-coder/toppy/compare/v1.2.0...v1.2.1) (2018-11-26) 125 | 126 | 127 | ### Bug Fixes 128 | 129 | * **docs:** set font icons path properly ([1d26809](https://github.com/lokesh-coder/toppy/commit/1d26809)) 130 | * **docs:** set font icons path properly ([0ae8424](https://github.com/lokesh-coder/toppy/commit/0ae8424)) 131 | 132 | # [1.2.0](https://github.com/lokesh-coder/toppy/compare/v1.1.1...v1.2.0) (2018-11-24) 133 | 134 | 135 | ### Features 136 | 137 | * added new config prop 'bodyClassNameOnOpen' ([0e047bf](https://github.com/lokesh-coder/toppy/commit/0e047bf)) 138 | * add new config 'closeOnEsc' ([fc1577e](https://github.com/lokesh-coder/toppy/commit/fc1577e)) 139 | * added support to change content after create ([02182b3](https://github.com/lokesh-coder/toppy/commit/02182b3)) 140 | * added version selector in doc ([7554f7b](https://github.com/lokesh-coder/toppy/commit/7554f7b)) 141 | * minor improvements and fixes ([8152b1c](https://github.com/lokesh-coder/toppy/commit/8152b1c)) 142 | 143 | ## [1.1.1](https://github.com/lokesh-coder/toppy/compare/v1.1.0...v1.1.1) (2018-11-22) 144 | 145 | 146 | ### Bug Fixes 147 | 148 | * multiple toppy config override ([#8](https://github.com/lokesh-coder/toppy/issues/8)) ([ca0ce7e](https://github.com/lokesh-coder/toppy/commit/ca0ce7e)) 149 | 150 | # [1.1.0](https://github.com/lokesh-coder/toppy/compare/v1.0.18...v1.1.0) (2018-11-18) 151 | 152 | 153 | ### Features 154 | 155 | * fade in content once position is updated ([3520ef6](https://github.com/lokesh-coder/toppy/commit/3520ef6)) 156 | * fade in content only after final position is updated ([abbfc8f](https://github.com/lokesh-coder/toppy/commit/abbfc8f)) 157 | 158 | ## [1.0.18](https://github.com/lokesh-coder/toppy/compare/v1.0.17...v1.0.18) (2018-11-16) 159 | 160 | ## [1.0.17](https://github.com/lokesh-coder/toppy/compare/v1.0.16...v1.0.17) (2018-11-13) 161 | 162 | ## [1.0.16](https://github.com/lokesh-coder/toppy/compare/v1.0.15...v1.0.16) (2018-11-09) 163 | 164 | ## [1.0.15](https://github.com/lokesh-coder/toppy/compare/v1.0.14...v1.0.15) (2018-11-06) 165 | 166 | ## [1.0.14](https://github.com/lokesh-coder/toppy/compare/v1.0.13...v1.0.14) (2018-11-03) 167 | 168 | ## [1.0.13](https://github.com/lokesh-coder/toppy/compare/v1.0.12...v1.0.13) (2018-11-02) 169 | 170 | 171 | ### Bug Fixes 172 | 173 | * override files on ghpages instead of replace ([d282266](https://github.com/lokesh-coder/toppy/commit/d282266)) 174 | 175 | ## [1.0.12](https://github.com/lokesh-coder/toppy/compare/v1.0.11...v1.0.12) (2018-11-02) 176 | 177 | 178 | ### Bug Fixes 179 | 180 | * add and sort details in package file ([6db7797](https://github.com/lokesh-coder/toppy/commit/6db7797)) 181 | * merge conflict resolve ([a412259](https://github.com/lokesh-coder/toppy/commit/a412259)) 182 | 183 | ## [1.0.11](https://github.com/lokesh-coder/toppy/compare/v1.0.10...v1.0.11) (2018-11-01) 184 | 185 | 186 | ### Bug Fixes 187 | 188 | * move archive logic to verfiyRelease stage ([cdce3c1](https://github.com/lokesh-coder/toppy/commit/cdce3c1)) 189 | 190 | ## [1.0.10](https://github.com/lokesh-coder/toppy/compare/v1.0.9...v1.0.10) (2018-11-01) 191 | 192 | 193 | ### Bug Fixes 194 | 195 | * archive version before publishing ([6dd714a](https://github.com/lokesh-coder/toppy/commit/6dd714a)) 196 | 197 | ## [1.0.9](https://github.com/lokesh-coder/toppy/compare/v1.0.8...v1.0.9) (2018-11-01) 198 | 199 | 200 | ### Bug Fixes 201 | 202 | * publish gh-pages with achived versions ([19a34c2](https://github.com/lokesh-coder/toppy/commit/19a34c2)) 203 | 204 | ## [1.0.8](https://github.com/lokesh-coder/toppy/compare/v1.0.7...v1.0.8) (2018-11-01) 205 | 206 | 207 | ### Bug Fixes 208 | 209 | * ghpages publish path ([f5d2f47](https://github.com/lokesh-coder/toppy/commit/f5d2f47)) 210 | 211 | ## [1.0.7](https://github.com/lokesh-coder/toppy/compare/v1.0.6...v1.0.7) (2018-11-01) 212 | 213 | 214 | ### Bug Fixes 215 | 216 | * set archive version properly ([b01d0ab](https://github.com/lokesh-coder/toppy/commit/b01d0ab)) 217 | 218 | ## [1.0.6](https://github.com/lokesh-coder/toppy/compare/v1.0.5...v1.0.6) (2018-11-01) 219 | 220 | 221 | ### Bug Fixes 222 | 223 | * archive doc build versions ([06226c4](https://github.com/lokesh-coder/toppy/commit/06226c4)) 224 | 225 | ## [1.0.5](https://github.com/lokesh-coder/toppy/compare/v1.0.4...v1.0.5) (2018-11-01) 226 | 227 | 228 | ### Bug Fixes 229 | 230 | * change gh pages publish package ([a5ef149](https://github.com/lokesh-coder/toppy/commit/a5ef149)) 231 | 232 | ## [1.0.4](https://github.com/lokesh-coder/toppy/compare/v1.0.3...v1.0.4) (2018-11-01) 233 | 234 | 235 | ### Bug Fixes 236 | 237 | * update build command in package json ([0ed8a10](https://github.com/lokesh-coder/toppy/commit/0ed8a10)) 238 | 239 | ## [1.0.3](https://github.com/lokesh-coder/toppy/compare/v1.0.2...v1.0.3) (2018-11-01) 240 | 241 | 242 | ### Bug Fixes 243 | 244 | * remove assets release in github ([8fb1ec8](https://github.com/lokesh-coder/toppy/commit/8fb1ec8)) 245 | 246 | ## [1.0.2](https://github.com/lokesh-coder/toppy/compare/v1.0.1...v1.0.2) (2018-11-01) 247 | 248 | 249 | ### Bug Fixes 250 | 251 | * release doc assets to github pages ([87b460b](https://github.com/lokesh-coder/toppy/commit/87b460b)) 252 | 253 | ## [1.0.1](https://github.com/lokesh-coder/toppy.git/compare/v1.0.0...v1.0.1) (2018-11-01) 254 | 255 | # 1.0.0 (2018-11-01) 256 | 257 | 258 | ### Bug Fixes 259 | 260 | * package json scripts and travis ([bc4f996](https://github.com/lokesh-coder/toppy.git/commit/bc4f996)) 261 | 262 | 263 | ### Features 264 | 265 | * add support for ghpages auto publish ([a8c8b7c](https://github.com/lokesh-coder/toppy.git/commit/a8c8b7c)) 266 | * initial commit ([6464f1a](https://github.com/lokesh-coder/toppy.git/commit/6464f1a)) 267 | * **docs:** installation API update ([a80fab0](https://github.com/lokesh-coder/toppy.git/commit/a80fab0)) 268 | --------------------------------------------------------------------------------