├── .auto-changelog ├── .editorconfig ├── .github ├── stale.yaml └── workflows │ ├── run-tests.sh │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Readme.md ├── angular.json ├── bin └── test.sh ├── capacitor.config.ts ├── cypress.config.ts ├── cypress ├── e2e │ └── spec.cy.ts └── tsconfig.json ├── img ├── android.gif └── ios.gif ├── ionic.config.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── projects └── ionic-header-parallax │ ├── ng-package.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── ionic-header-parallax.directive.ts │ │ └── ionic-header-parallax.module.ts │ ├── public-api.ts │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ └── tsconfig.spec.json ├── src ├── app │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.routes.ts │ └── home │ │ ├── home-routing.module.ts │ │ ├── home.module.ts │ │ ├── home.page.html │ │ ├── home.page.scss │ │ └── home.page.ts ├── assets │ ├── icon │ │ └── favicon.png │ └── shapes.svg ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── global.scss ├── index.html ├── main.ts ├── polyfills.ts ├── test.ts ├── theme │ └── variables.scss └── zone-flags.ts ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json /.auto-changelog: -------------------------------------------------------------------------------- 1 | { 2 | "stdout": false, 3 | "ignoreCommitPattern": "^chore[:\\(]|^docs?[:\\(]|^build[:\\(]|^ci[:\\(]", 4 | "breakingPattern": "!:" 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.github/stale.yaml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 15 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.github/workflows/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | PKG_VERSION=$(cd $GITHUB_WORKSPACE/dist/ionic-header-parallax && npm pkg get version | tr -d '"') 3 | mkdir cypress 4 | cp -r $GITHUB_WORKSPACE/cypress/* ./cypress 5 | cp $GITHUB_WORKSPACE/cypress.config.ts . 6 | cp -r $GITHUB_WORKSPACE/src/app/home/* ./src/app/home 7 | npm i $GITHUB_WORKSPACE/dist/ionic-header-parallax-$PKG_VERSION.tgz 8 | cypress install 9 | ng serve --host 0.0.0.0 --port 4200 & \ 10 | (wait-on http://0.0.0.0:4200 --timeout 30000 --interval 1000 && cypress run --headless) \ 11 | || { echo 'Angular server failed to start'; exit 1; } -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test-angular19: 7 | runs-on: ubuntu-latest 8 | container: 9 | image: raschidjfr/ionic-blank:ionic8-angular19-cypress14 10 | 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Install dependencies 16 | run: npm ci 17 | 18 | - name: Build project 19 | run: npm run build 20 | 21 | - name: Run tests 22 | working-directory: /app 23 | run: bash $GITHUB_WORKSPACE/.github/workflows/run-tests.sh 24 | 25 | test-angular16: 26 | runs-on: ubuntu-latest 27 | container: 28 | image: raschidjfr/ionic-blank:ionic8-angular16-cypress14 29 | 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v4 33 | 34 | - name: Install dependencies 35 | run: npm ci 36 | 37 | - name: Build project 38 | run: npm run build 39 | 40 | - name: Run tests 41 | working-directory: /app 42 | run: bash $GITHUB_WORKSPACE/.github/workflows/run-tests.sh 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | *~ 5 | *.sw[mnpcod] 6 | .tmp 7 | *.tmp 8 | *.tmp.* 9 | *.sublime-project 10 | *.sublime-workspace 11 | .DS_Store 12 | Thumbs.db 13 | UserInterfaceState.xcuserstate 14 | $RECYCLE.BIN/ 15 | 16 | *.log 17 | log.txt 18 | npm-debug.log* 19 | 20 | /.idea 21 | /.ionic 22 | /.sass-cache 23 | /.sourcemaps 24 | /.versions 25 | /.vscode 26 | /coverage 27 | /dist 28 | node_modules/ 29 | /platforms 30 | /plugins 31 | /www 32 | /ios 33 | /android 34 | 35 | .angular 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Changelog 2 | 3 | All notable changes to this project will be documented in this file. Dates are displayed in UTC. 4 | 5 | Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). 6 | 7 | #### [6.1.2](https://github.com/RaschidJFR/ionic-header-parallax/compare/6.1.1...6.1.2) 8 | 9 | > 13 March 2025 10 | 11 | - fix: exception on initialization for NgModule projects [`#55`](https://github.com/RaschidJFR/ionic-header-parallax/pull/55) 12 | 13 | #### [6.1.1](https://github.com/RaschidJFR/ionic-header-parallax/compare/6.1.0...6.1.1) 14 | 15 | > 8 March 2025 16 | 17 | - fix: error `<ion-toolbar> element is needed…` in standalone [`#50`](https://github.com/RaschidJFR/ionic-header-parallax/issues/50) 18 | 19 | #### [6.1.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/6.0.0...6.1.0) 20 | 21 | > 1 March 2025 22 | 23 | - refactor(demo): migrate demo app to angular standalone [`f76f791`](https://github.com/RaschidJFR/ionic-header-parallax/commit/f76f791050d14170381288e5453368bc2bea73ce) 24 | - refactor(directive): make `standalone` [`17621ff`](https://github.com/RaschidJFR/ionic-header-parallax/commit/17621ffe45da5fd1bbde478bf76bd8e41774199e) 25 | 26 | ### [6.0.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/5.0.0...6.0.0) 27 | 28 | > 26 October 2024 29 | 30 | - fix: update directive to support Ionic8 [`#51`](https://github.com/RaschidJFR/ionic-header-parallax/issues/51) 31 | 32 | ### [5.0.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/4.0.0...5.0.0) 33 | 34 | > 23 June 2023 35 | 36 | - **Breaking change:** feat(ionic)!: upgrade to Ionic7 and Angular v14 [`658510e`](https://github.com/RaschidJFR/ionic-header-parallax/commit/658510eeca9c2d38f0ca93b6c384a40c4eacb62b) 37 | 38 | ### [4.0.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/3.1.0...4.0.0) 39 | 40 | > 23 June 2023 41 | 42 | - **Breaking change:** feat(angular)!: add support for v13 [`#48`](https://github.com/RaschidJFR/ionic-header-parallax/pull/48) 43 | 44 | #### [3.1.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/3.0.2...3.1.0) 45 | 46 | > 9 December 2021 47 | 48 | - fix(height): string-type inputs not correctly parsed [`#44`](https://github.com/RaschidJFR/ionic-header-parallax/pull/44) 49 | - fix(height): string-type inputs not correctly parsed (#44) [`#40`](https://github.com/RaschidJFR/ionic-header-parallax/issues/40) 50 | 51 | ### [3.0.2](https://github.com/RaschidJFR/ionic-header-parallax/compare/3.0.0-rc...3.0.2) 52 | 53 | > 30 November 2021 54 | 55 | - feat: enhanced error handling [`6a69c3a`](https://github.com/RaschidJFR/ionic-header-parallax/commit/6a69c3a998f507f205a7a00e6fbc670e2bc12bb1) 56 | 57 | #### [3.0.0-rc](https://github.com/RaschidJFR/ionic-header-parallax/compare/3.0.0-dev...3.0.0-rc) 58 | 59 | > 30 November 2021 60 | 61 | - **Breaking change:** refactor: redo for Ionic 5 and Angular 12 [`#39`](https://github.com/RaschidJFR/ionic-header-parallax/pull/39) 62 | - **Breaking change:** refactor: redo for Ionic 5 and Angular 12 (#39) [`#34`](https://github.com/RaschidJFR/ionic-header-parallax/issues/34) 63 | 64 | #### [3.0.0-dev](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.2.2...3.0.0-dev) 65 | 66 | > 31 May 2021 67 | 68 | - fix: recursive initializer to avoid error when view not ready yet [`#29`](https://github.com/RaschidJFR/ionic-header-parallax/pull/29) 69 | - refactor(app): remove unused native modules [`f3cada4`](https://github.com/RaschidJFR/ionic-header-parallax/commit/f3cada48e7bdcf1334f3d9cb991b16758f252474) 70 | 71 | #### [2.2.2](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.2.1...2.2.2) 72 | 73 | > 30 August 2019 74 | 75 | #### [2.2.1](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.2.0...2.2.1) 76 | 77 | > 30 August 2019 78 | 79 | - fix: Error "Parallax directive requires an <ion-content>..." on init [`5f38280`](https://github.com/RaschidJFR/ionic-header-parallax/commit/5f38280f0f81ba4cd5a9699362eb47f29432fc67) 80 | 81 | #### [2.2.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.1.4...2.2.0) 82 | 83 | > 30 August 2019 84 | 85 | - feat(demo): added angular demo project [`baa0157`](https://github.com/RaschidJFR/ionic-header-parallax/commit/baa0157ed83050bc134a4d9c04a37b41f9d66e4c) 86 | 87 | #### [2.1.4](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.1.3...2.1.4) 88 | 89 | > 19 August 2019 90 | 91 | - improve(readme): added contact details [`fb8fecc`](https://github.com/RaschidJFR/ionic-header-parallax/commit/fb8fecc37ef240420988be9a9afae6c02e0ba7c9) 92 | 93 | #### [2.1.3](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.1.2...2.1.3) 94 | 95 | > 26 June 2019 96 | 97 | - Updated Readme [`e1ff8b5`](https://github.com/RaschidJFR/ionic-header-parallax/commit/e1ff8b544763a06e25b43e59669af8958f3b6978) 98 | 99 | #### [2.1.2](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.1.1...2.1.2) 100 | 101 | > 21 June 2019 102 | 103 | - Updated Readme [`68f27bb`](https://github.com/RaschidJFR/ionic-header-parallax/commit/68f27bb50eb64434464bd22d42f98398dee865c9) 104 | 105 | #### [2.1.1](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.1.0...2.1.1) 106 | 107 | > 21 June 2019 108 | 109 | - Fixed issue #8: titleColor not working as expected... [`3ea572e`](https://github.com/RaschidJFR/ionic-header-parallax/commit/3ea572e11544e591b816cc66a1ddd998316a02e2) 110 | 111 | #### [2.1.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.0.1...2.1.0) 112 | 113 | > 14 June 2019 114 | 115 | - [fix] Runtime error when either `<ion-title>` or `<ion-buttons>` was missing. [`9189baf`](https://github.com/RaschidJFR/ionic-header-parallax/commit/9189baf3b5ff5bb46c845576a3ab2c38fece2c64) 116 | 117 | #### [2.0.1](https://github.com/RaschidJFR/ionic-header-parallax/compare/2.0.0...2.0.1) 118 | 119 | > 9 June 2019 120 | 121 | - Readme editted [`de13fbd`](https://github.com/RaschidJFR/ionic-header-parallax/commit/de13fbd92b5628d7a002dd09f45fcec4cb202058) 122 | 123 | ### [2.0.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/1.1.0...2.0.0) 124 | 125 | > 9 June 2019 126 | 127 | #### [1.1.0](https://github.com/RaschidJFR/ionic-header-parallax/compare/1.0.1...1.1.0) 128 | 129 | > 26 June 2019 130 | 131 | - Fixed #6: Cannot find name "ParallaxDirective" [`#6`](https://github.com/RaschidJFR/ionic-header-parallax/issues/6) 132 | - Upgraded to v4 as angular lib [`816ce45`](https://github.com/RaschidJFR/ionic-header-parallax/commit/816ce45a6fd8ba5d2d15204e3acde12b635529f5) 133 | - [fix] Runtime error when either `<ion-title>` or `<ion-buttons>` was missing. [`9189baf`](https://github.com/RaschidJFR/ionic-header-parallax/commit/9189baf3b5ff5bb46c845576a3ab2c38fece2c64) 134 | - Fixed issue #8: titleColor not working as expected... [`3ea572e`](https://github.com/RaschidJFR/ionic-header-parallax/commit/3ea572e11544e591b816cc66a1ddd998316a02e2) 135 | 136 | #### 1.0.1 137 | 138 | > 6 June 2019 139 | 140 | - first commit [`da7a282`](https://github.com/RaschidJFR/ionic-header-parallax/commit/da7a282187d7c32108bf342820e0282f1e0f25d7) 141 | - Update Readme.md [`9693c95`](https://github.com/RaschidJFR/ionic-header-parallax/commit/9693c9520ae1137e0878657f2f0b4d6eae77ab1a) 142 | - Added gif to readme [`7d82ea1`](https://github.com/RaschidJFR/ionic-header-parallax/commit/7d82ea190718e252718260cccb29f51b03a91156) 143 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | * You'll find the parallax directive's source in [`/projects/ionic-header-parallax/src`](projects/ionic-header-parallax/src). 4 | * The source code for the demo app [`/src`](src). 5 | 6 | ## Getting Started 7 | 8 | In the root folder: 9 | 1. Run `npm run watch` to build the directive in watch mode. 10 | 1. Run `npm run link` to point the demo app to use the local directive's source. 11 | 2. Run `ng serve` to launch the demo app. 12 | 13 | ### Test 14 | 1. Build: `npm run build` 15 | 2. Test with Docker: `bin/test.sh` 16 | 17 | 18 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Parallax Header Directive for Ionic 2 | 3 | This directive adds a parallax effect to `ion-header`, displaying a cover photo that transitions to a toolbar on scroll. 4 | 5 | ### Version Compatibility 6 | 7 | | Stack | Tag | 8 | | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | 9 | | ![](https://img.shields.io/badge/-v8-white?logo=ionic) ![](https://img.shields.io/badge/-v16--19-red?logo=angular) | [6.x.x](https://www.npmjs.com/package/ionic-header-parallax?activeTab=versions) | 10 | | ![](https://img.shields.io/badge/-v7-white?logo=ionic) ![](https://img.shields.io/badge/-v14-red?logo=angular) | [5.x.x](https://www.npmjs.com/package/ionic-header-parallax?activeTab=versions) | 11 | | ![](https://img.shields.io/badge/-v6-white?logo=ionic) ![](https://img.shields.io/badge/-v13-red?logo=angular) | [4.x.x](https://www.npmjs.com/package/ionic-header-parallax?activeTab=versions) | 12 | | ![](https://img.shields.io/badge/-v5-white?logo=ionic) ![](https://img.shields.io/badge/-v12-red?logo=angular) | [3.x.x](https://www.npmjs.com/package/ionic-header-parallax?activeTab=versions) | 13 | | ![](https://img.shields.io/badge/-v4-white?logo=ionic) ![](https://img.shields.io/badge/-v8-red?logo=angular) | [2.x.x](https://www.npmjs.com/package/ionic-header-parallax?activeTab=versions) | 14 | 15 | ## Live Demo 16 | 17 | Checkout the Live Demo [here](https://raschidjfr.github.io/ionic-header-parallax) 18 | 19 | | iOS | Android | 20 | | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | 21 | | ![](https://raw.githubusercontent.com/raschidJFR/ionic-header-parallax/5.0.0/img/ios.gif) | ![](https://raw.githubusercontent.com/raschidJFR/ionic-header-parallax/5.0.0/img/android.gif) | 22 | 23 | ## Set Up 24 | 1. Install package: `npm i ionic-header-parallax`. 25 | 2. Import the directive into your desired **Module**: 26 | 27 | ```ts 28 | // app.module.ts 29 | 30 | import { IonicHeaderParallaxModule } from 'ionic-header-parallax'; 31 | 32 | @NgModule({ 33 | imports: [ 34 | IonicHeaderParallaxModule, // <- 35 | ... 36 | ``` 37 | …or standalone **Component**: 38 | ```ts 39 | // my-standalone-component.page.ts 40 | 41 | import { ParallaxDirective } from 'ionic-header-parallax'; 42 | 43 | @Component({ 44 | imports: [ 45 | ParallaxDirective, // <- 46 | ... 47 | ``` 48 | 49 | ## Usage 50 | 51 | Just add the attribute `parallax` to any `` element: 52 | 53 | ```html 54 | 60 | 61 | Parallax Header 62 | 63 | 64 | 65 | Some content here 66 | ``` 67 | 68 | | Parameter | Type | Description | 69 | | ---------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | 70 | | imageUrl | `string` | The background image to show while expanded. | 71 | | height | ` number \| string` | The height for the header when expanded. If the value is a number, it will be set in `px`. If the value is a string it will be passed as is (eg: `"20rem"`) | 72 | | color | `string` | The color (web hex formatted) to show while the header is expanded when no `imageUrl` is set. When scrolled, it will fade to the toolbar's color. | 73 | | bgPosition | `'top' \| 'center' \| 'bottom'` | The position of the image in the header. This parameter slightly changes the feeling of the animation. Default: `'top'` | 74 | 75 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "app": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@ionic/angular-toolkit:page": { 10 | "styleext": "scss", 11 | "standalone": true 12 | } 13 | }, 14 | "root": "", 15 | "sourceRoot": "src", 16 | "prefix": "app", 17 | "architect": { 18 | "build": { 19 | "builder": "@angular-devkit/build-angular:application", 20 | "options": { 21 | "outputPath": { 22 | "base": "www", 23 | "browser": "" 24 | }, 25 | "index": "src/index.html", 26 | "polyfills": [ 27 | "src/polyfills.ts" 28 | ], 29 | "tsConfig": "tsconfig.app.json", 30 | "inlineStyleLanguage": "scss", 31 | "assets": [ 32 | { 33 | "glob": "**/*", 34 | "input": "src/assets", 35 | "output": "assets" 36 | } 37 | ], 38 | "styles": ["src/global.scss", "src/theme/variables.scss"], 39 | "scripts": [], 40 | "browser": "src/main.ts" 41 | }, 42 | "configurations": { 43 | "production": { 44 | "budgets": [ 45 | { 46 | "type": "initial", 47 | "maximumWarning": "2mb", 48 | "maximumError": "5mb" 49 | }, 50 | { 51 | "type": "anyComponentStyle", 52 | "maximumWarning": "2kb", 53 | "maximumError": "4kb" 54 | } 55 | ], 56 | "fileReplacements": [ 57 | { 58 | "replace": "src/environments/environment.ts", 59 | "with": "src/environments/environment.prod.ts" 60 | } 61 | ], 62 | "outputHashing": "all" 63 | }, 64 | "development": { 65 | "optimization": false, 66 | "extractLicenses": false, 67 | "sourceMap": true, 68 | "namedChunks": true 69 | }, 70 | "ci": { 71 | "progress": false 72 | } 73 | }, 74 | "defaultConfiguration": "production" 75 | }, 76 | "serve": { 77 | "builder": "@angular-devkit/build-angular:dev-server", 78 | "configurations": { 79 | "production": { 80 | "buildTarget": "app:build:production" 81 | }, 82 | "development": { 83 | "buildTarget": "app:build:development" 84 | }, 85 | "ci": { 86 | "progress": false 87 | } 88 | }, 89 | "defaultConfiguration": "development" 90 | }, 91 | "extract-i18n": { 92 | "builder": "@angular-devkit/build-angular:extract-i18n", 93 | "options": { 94 | "buildTarget": "app:build" 95 | } 96 | }, 97 | "test": { 98 | "builder": "@angular-devkit/build-angular:karma", 99 | "options": { 100 | "main": "src/test.ts", 101 | "polyfills": "src/polyfills.ts", 102 | "tsConfig": "tsconfig.spec.json", 103 | "karmaConfig": "karma.conf.js", 104 | "inlineStyleLanguage": "scss", 105 | "assets": [ 106 | { 107 | "glob": "**/*", 108 | "input": "src/assets", 109 | "output": "assets" 110 | } 111 | ], 112 | "styles": ["src/global.scss", "src/theme/variables.scss"], 113 | "scripts": [] 114 | }, 115 | "configurations": { 116 | "ci": { 117 | "progress": false, 118 | "watch": false 119 | } 120 | } 121 | }, 122 | "lint": { 123 | "builder": "@angular-eslint/builder:lint", 124 | "options": { 125 | "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] 126 | } 127 | } 128 | } 129 | }, 130 | "ionic-header-parallax": { 131 | "projectType": "library", 132 | "root": "projects/ionic-header-parallax", 133 | "sourceRoot": "projects/ionic-header-parallax/src", 134 | "prefix": "lib", 135 | "architect": { 136 | "build": { 137 | "builder": "@angular-devkit/build-angular:ng-packagr", 138 | "options": { 139 | "project": "projects/ionic-header-parallax/ng-package.json" 140 | }, 141 | "configurations": { 142 | "production": { 143 | "tsConfig": "projects/ionic-header-parallax/tsconfig.lib.prod.json" 144 | }, 145 | "development": { 146 | "tsConfig": "projects/ionic-header-parallax/tsconfig.lib.json" 147 | } 148 | }, 149 | "defaultConfiguration": "production" 150 | }, 151 | "test": { 152 | "builder": "@angular-devkit/build-angular:karma", 153 | "options": { 154 | "tsConfig": "projects/ionic-header-parallax/tsconfig.spec.json", 155 | "polyfills": [ 156 | "zone.js", 157 | "zone.js/testing" 158 | ] 159 | } 160 | } 161 | } 162 | } 163 | }, 164 | "cli": { 165 | "schematicCollections": ["@ionic/angular-toolkit"] 166 | }, 167 | "schematics": { 168 | "@ionic/angular-toolkit:component": { 169 | "styleext": "scss" 170 | }, 171 | "@ionic/angular-toolkit:page": { 172 | "styleext": "scss" 173 | }, 174 | "@angular-eslint/schematics:application": { 175 | "setParserOptionsProject": true 176 | }, 177 | "@angular-eslint/schematics:library": { 178 | "setParserOptionsProject": true 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /bin/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Use this to test the angular project with cypress. 3 | # Execute from the project's root folder. 4 | # Usage: bin/test.sh [angular_version] 5 | 6 | get_lib_version() { 7 | echo $(cd dist/ionic-header-parallax && npm pkg get version) 8 | } 9 | 10 | run_test() { 11 | ng_version=$1 12 | lib_version=$(get_lib_version) 13 | docker run \ 14 | --rm \ 15 | -ti \ 16 | -v "$PWD:/project" \ 17 | -v "$PWD/dist:/dist" \ 18 | raschidjfr/ionic-blank:ionic8-angular$ng_version-cypress14 \ 19 | bash -c "\ 20 | mkdir cypress \ 21 | && cp -r /project/cypress/* ./cypress \ 22 | && cp /project/cypress.config.ts . \ 23 | && cp -r /project/src/app/home/* ./src/app/home \ 24 | && npm i /dist/ionic-header-parallax-$lib_version.tgz \ 25 | && (ng serve --host 0.0.0.0 --port 4200 \ 26 | & (wait-on http://0.0.0.0:4200 && cypress run --headless) \ 27 | || { echo 'Angular server failed to start'; exit 1; })" 28 | } 29 | 30 | echo "Testing library in Ionic v8 and Angular v19..." 31 | run_test 19 32 | echo "Ionic v8 and Angular v19 tests successful!" 33 | echo "Testing library in Ionic v8 and Angular v16..." 34 | run_test 16 35 | echo "Ionic v8 and Angular v16 tests successful!" -------------------------------------------------------------------------------- /capacitor.config.ts: -------------------------------------------------------------------------------- 1 | import type { CapacitorConfig } from '@capacitor/cli'; 2 | 3 | const config: CapacitorConfig = { 4 | appId: 'io.ionic.starter', 5 | appName: 'demo', 6 | webDir: 'www' 7 | }; 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress' 2 | 3 | export default defineConfig({ 4 | 5 | e2e: { 6 | baseUrl: 'http://0.0.0.0:4200', 7 | supportFile: false 8 | }, 9 | 10 | 11 | component: { 12 | devServer: { 13 | framework: 'angular', 14 | bundler: 'webpack', 15 | }, 16 | specPattern: '**/*.cy.ts' 17 | } 18 | 19 | }) 20 | -------------------------------------------------------------------------------- /cypress/e2e/spec.cy.ts: -------------------------------------------------------------------------------- 1 | describe('Ionic Header Parallax', () => { 2 | it('Main component can load', () => { 3 | cy.visit('/'); 4 | cy.contains('Start with Ionic UI Components'); 5 | }); 6 | 7 | it('Parallax effect works', () => { 8 | cy.visit('/'); 9 | cy.wait(200); 10 | 11 | cy.get('ion-header').should('have.css', 'height', '300px'); 12 | cy.get('ion-content').shadow().find('.inner-scroll').scrollTo('bottom'); 13 | cy.get('ion-header').invoke('height').should('be.lessThan', 60); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"], 4 | "compilerOptions": { 5 | "sourceMap": false, 6 | "types": ["cypress"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /img/android.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaschidJFR/ionic-header-parallax/8b9aba588794641e2e67f3a6c0c20abe7e8e3925/img/android.gif -------------------------------------------------------------------------------- /img/ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaschidJFR/ionic-header-parallax/8b9aba588794641e2e67f3a6c0c20abe7e8e3925/img/ios.gif -------------------------------------------------------------------------------- /ionic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "integrations": { 4 | "capacitor": {} 5 | }, 6 | "type": "angular-standalone" 7 | } 8 | -------------------------------------------------------------------------------- /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'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/app'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "6.0.0", 4 | "author": "Ionic Framework", 5 | "homepage": "https://ionicframework.com/", 6 | "scripts": { 7 | "start": "ng serve", 8 | "prebuild": "(cd projects/ionic-header-parallax && npm i)", 9 | "build": "ng build ionic-header-parallax -c production", 10 | "postbuild": "(cd dist/ionic-header-parallax && npm pack ----pack-destination ..)", 11 | "link": "(cd dist/ionic-header-parallax && npm link) && npm link ionic-header-parallax", 12 | "watch": "ng build ionic-header-parallax --watch", 13 | "publish": "cd dist/ionic-header-parallax && cp ../../Readme.md . && npm publish", 14 | "build:demo": "ng build --base-href=\".\" -c production", 15 | "predeploy": "npm run build", 16 | "deploy": "npm run build:demo && npx ngh --dir=dist/demo", 17 | "postdeploy": "npm run changelog", 18 | "changelog": "npx --yes auto-changelog", 19 | "test": "bin/test.sh" 20 | }, 21 | "private": true, 22 | "dependencies": { 23 | "@angular/animations": "^19.0.0", 24 | "@angular/common": "^19.0.0", 25 | "@angular/compiler": "^19.0.0", 26 | "@angular/core": "^19.0.0", 27 | "@angular/forms": "^19.0.0", 28 | "@angular/platform-browser": "^19.0.0", 29 | "@angular/platform-browser-dynamic": "^19.0.0", 30 | "@angular/router": "^19.0.0", 31 | "@capacitor/app": "7.0.0", 32 | "@capacitor/core": "7.0.1", 33 | "@capacitor/haptics": "7.0.0", 34 | "@capacitor/keyboard": "7.0.0", 35 | "@capacitor/status-bar": "7.0.0", 36 | "@ionic/angular": "^8.0.0", 37 | "ionicons": "^7.0.0", 38 | "rxjs": "~7.8.0", 39 | "tslib": "^2.3.0", 40 | "zone.js": "~0.15.0" 41 | }, 42 | "devDependencies": { 43 | "@angular-devkit/build-angular": "^19.0.0", 44 | "@angular-eslint/builder": "^19.0.0", 45 | "@angular-eslint/eslint-plugin": "^19.0.0", 46 | "@angular-eslint/eslint-plugin-template": "^19.0.0", 47 | "@angular-eslint/schematics": "^19.0.0", 48 | "@angular-eslint/template-parser": "^19.0.0", 49 | "@angular/cli": "^19.0.0", 50 | "@angular/compiler-cli": "^19.0.0", 51 | "@angular/language-service": "^19.0.0", 52 | "@capacitor/cli": "7.0.1", 53 | "@ionic/angular-toolkit": "^12.0.0", 54 | "@types/jasmine": "~5.1.0", 55 | "@typescript-eslint/eslint-plugin": "^8.18.0", 56 | "@typescript-eslint/parser": "^8.18.0", 57 | "cypress": "^14.1.0", 58 | "eslint": "^9.16.0", 59 | "eslint-plugin-import": "^2.29.1", 60 | "eslint-plugin-jsdoc": "^48.2.1", 61 | "eslint-plugin-prefer-arrow": "1.2.2", 62 | "jasmine-core": "~5.1.0", 63 | "jasmine-spec-reporter": "~5.0.0", 64 | "karma": "~6.4.0", 65 | "karma-chrome-launcher": "~3.2.0", 66 | "karma-coverage": "~2.2.0", 67 | "karma-jasmine": "~5.1.0", 68 | "karma-jasmine-html-reporter": "~2.1.0", 69 | "ng-packagr": "^19.2.0", 70 | "typescript": "~5.6.3" 71 | }, 72 | "description": "An Ionic project" 73 | } 74 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ionic-header-parallax", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | }, 7 | "allowedNonPeerDependencies": ["to-px"] 8 | } -------------------------------------------------------------------------------- /projects/ionic-header-parallax/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-header-parallax", 3 | "version": "6.1.2", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ionic-header-parallax", 9 | "version": "6.1.2", 10 | "license": "ISC", 11 | "dependencies": { 12 | "to-px": "^1.1.0", 13 | "tslib": "^2.3.0" 14 | }, 15 | "devDependencies": { 16 | "@types/to-px": "^1.1.4" 17 | }, 18 | "peerDependencies": { 19 | "@angular/common": ">=14.0.0", 20 | "@angular/core": ">=14.0.0", 21 | "@ionic/angular": ">=7.0.0" 22 | } 23 | }, 24 | "node_modules/@angular/common": { 25 | "version": "19.2.2", 26 | "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.2.2.tgz", 27 | "integrity": "sha512-tLb4/gAjy1WFYZd68FnwLaC8w3b10RG8r+x5MUnbyhJa9K3qILzF8gWUVJ4tKLKentB3fWPWPNs9OFZH5XVf1g==", 28 | "license": "MIT", 29 | "peer": true, 30 | "dependencies": { 31 | "tslib": "^2.3.0" 32 | }, 33 | "engines": { 34 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 35 | }, 36 | "peerDependencies": { 37 | "@angular/core": "19.2.2", 38 | "rxjs": "^6.5.3 || ^7.4.0" 39 | } 40 | }, 41 | "node_modules/@angular/core": { 42 | "version": "19.2.2", 43 | "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.2.2.tgz", 44 | "integrity": "sha512-RL2wxk62BREW4sksnBSjtBDgPxHqIJQKkrlxXbbu2jZql7xohEQtdTZP3DB6iWnZLYgz8CAfTzyaHMvj6HfdLg==", 45 | "license": "MIT", 46 | "peer": true, 47 | "dependencies": { 48 | "tslib": "^2.3.0" 49 | }, 50 | "engines": { 51 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 52 | }, 53 | "peerDependencies": { 54 | "rxjs": "^6.5.3 || ^7.4.0", 55 | "zone.js": "~0.15.0" 56 | } 57 | }, 58 | "node_modules/@angular/forms": { 59 | "version": "19.2.2", 60 | "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.2.2.tgz", 61 | "integrity": "sha512-Cin+VdP4SbSZU1VQvZLkIJ6j0srGoWHG/yS7HjsVRNGQHT9kRGi/sz8DE0wHYspLPkq0dGgkVFUcjzDKs3O+wA==", 62 | "license": "MIT", 63 | "peer": true, 64 | "dependencies": { 65 | "tslib": "^2.3.0" 66 | }, 67 | "engines": { 68 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 69 | }, 70 | "peerDependencies": { 71 | "@angular/common": "19.2.2", 72 | "@angular/core": "19.2.2", 73 | "@angular/platform-browser": "19.2.2", 74 | "rxjs": "^6.5.3 || ^7.4.0" 75 | } 76 | }, 77 | "node_modules/@angular/platform-browser": { 78 | "version": "19.2.2", 79 | "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.2.2.tgz", 80 | "integrity": "sha512-D15zwFiOczbPhJrdCgbuZwSDwQb6uyDjM5YI3SgIFIxP2JiVdBg3AvmqoJQOuU8ntMeeiOxtdl8nqcvUddhdPw==", 81 | "license": "MIT", 82 | "peer": true, 83 | "dependencies": { 84 | "tslib": "^2.3.0" 85 | }, 86 | "engines": { 87 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 88 | }, 89 | "peerDependencies": { 90 | "@angular/animations": "19.2.2", 91 | "@angular/common": "19.2.2", 92 | "@angular/core": "19.2.2" 93 | }, 94 | "peerDependenciesMeta": { 95 | "@angular/animations": { 96 | "optional": true 97 | } 98 | } 99 | }, 100 | "node_modules/@angular/router": { 101 | "version": "19.2.2", 102 | "resolved": "https://registry.npmjs.org/@angular/router/-/router-19.2.2.tgz", 103 | "integrity": "sha512-H0JEN1cs7uX++piCyT2gfhgqONceCKbRRZq+zr1T61c5heAi2pBGHuuZnU0D1d0sgA82CGcWFrTduUJg5wWamw==", 104 | "license": "MIT", 105 | "peer": true, 106 | "dependencies": { 107 | "tslib": "^2.3.0" 108 | }, 109 | "engines": { 110 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 111 | }, 112 | "peerDependencies": { 113 | "@angular/common": "19.2.2", 114 | "@angular/core": "19.2.2", 115 | "@angular/platform-browser": "19.2.2", 116 | "rxjs": "^6.5.3 || ^7.4.0" 117 | } 118 | }, 119 | "node_modules/@ionic/angular": { 120 | "version": "8.4.3", 121 | "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-8.4.3.tgz", 122 | "integrity": "sha512-dIrO7luoj9Iepo4tYKTzhCEpVFWIqZ1RNgDPttKVHa8hE4xhvbb1y2Ou9zmpz7cI5QShAkr3tnAbb7rkYh4fFQ==", 123 | "license": "MIT", 124 | "peer": true, 125 | "dependencies": { 126 | "@ionic/core": "8.4.3", 127 | "ionicons": "^7.0.0", 128 | "jsonc-parser": "^3.0.0", 129 | "tslib": "^2.3.0" 130 | }, 131 | "peerDependencies": { 132 | "@angular/core": ">=16.0.0", 133 | "@angular/forms": ">=16.0.0", 134 | "@angular/router": ">=16.0.0", 135 | "rxjs": ">=7.5.0", 136 | "zone.js": ">=0.13.0" 137 | } 138 | }, 139 | "node_modules/@ionic/core": { 140 | "version": "8.4.3", 141 | "resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.4.3.tgz", 142 | "integrity": "sha512-U9HdZ32bre6OKA5akJVmQMxNB8Art3Nqdn3s7m2W83I5NhLg9Tehaf8ua8jxPZtxCa1nuN7tUbzHmMCkcdqDTw==", 143 | "license": "MIT", 144 | "peer": true, 145 | "dependencies": { 146 | "@stencil/core": "4.20.0", 147 | "ionicons": "^7.2.2", 148 | "tslib": "^2.1.0" 149 | } 150 | }, 151 | "node_modules/@stencil/core": { 152 | "version": "4.20.0", 153 | "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.20.0.tgz", 154 | "integrity": "sha512-WPrTHFngvN081RY+dJPneKQLwnOFD60OMCOQGmmSHfCW0f4ujPMzzhwWU1gcSwXPWXz5O+8cBiiCaxAbJU7kAg==", 155 | "license": "MIT", 156 | "peer": true, 157 | "bin": { 158 | "stencil": "bin/stencil" 159 | }, 160 | "engines": { 161 | "node": ">=16.0.0", 162 | "npm": ">=7.10.0" 163 | } 164 | }, 165 | "node_modules/@types/to-px": { 166 | "version": "1.1.4", 167 | "resolved": "https://registry.npmjs.org/@types/to-px/-/to-px-1.1.4.tgz", 168 | "integrity": "sha512-t8imv37xCZ5yS7ff9g8/3iRwfqx6ro8h3qkI1F1gjqdeu7q5/CxyjDs/EIHbdoEH89SdGuUC+SU27CWUZQ/Jtg==", 169 | "dev": true, 170 | "license": "MIT" 171 | }, 172 | "node_modules/ionicons": { 173 | "version": "7.4.0", 174 | "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.4.0.tgz", 175 | "integrity": "sha512-ZK94MMqgzMCPPMhmk8Ouu6goyVHFIlw/ACP6oe3FrikcI0N7CX0xcwVaEbUc0G/v3W0shI93vo+9ve/KpvcNhQ==", 176 | "license": "MIT", 177 | "peer": true, 178 | "dependencies": { 179 | "@stencil/core": "^4.0.3" 180 | } 181 | }, 182 | "node_modules/jsonc-parser": { 183 | "version": "3.3.1", 184 | "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", 185 | "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", 186 | "license": "MIT", 187 | "peer": true 188 | }, 189 | "node_modules/parse-unit": { 190 | "version": "1.0.1", 191 | "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz", 192 | "integrity": "sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg==", 193 | "license": "MIT" 194 | }, 195 | "node_modules/rxjs": { 196 | "version": "7.8.2", 197 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", 198 | "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", 199 | "license": "Apache-2.0", 200 | "peer": true, 201 | "dependencies": { 202 | "tslib": "^2.1.0" 203 | } 204 | }, 205 | "node_modules/to-px": { 206 | "version": "1.1.0", 207 | "resolved": "https://registry.npmjs.org/to-px/-/to-px-1.1.0.tgz", 208 | "integrity": "sha512-bfg3GLYrGoEzrGoE05TAL/Uw+H/qrf2ptr9V3W7U0lkjjyYnIfgxmVLUfhQ1hZpIQwin81uxhDjvUkDYsC0xWw==", 209 | "license": "MIT", 210 | "dependencies": { 211 | "parse-unit": "^1.0.1" 212 | } 213 | }, 214 | "node_modules/tslib": { 215 | "version": "2.8.1", 216 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 217 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 218 | "license": "0BSD" 219 | }, 220 | "node_modules/zone.js": { 221 | "version": "0.15.0", 222 | "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.0.tgz", 223 | "integrity": "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==", 224 | "license": "MIT", 225 | "peer": true 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-header-parallax", 3 | "version": "6.1.2", 4 | "description": "Adds a parallax effect to `ion-header`, displaying a cover photo that transitions to a toolbar on scroll.", 5 | "peerDependencies": { 6 | "@angular/common": ">=14.0.0", 7 | "@angular/core": ">=14.0.0", 8 | "@ionic/angular": ">=7.0.0" 9 | }, 10 | "dependencies": { 11 | "to-px": "^1.1.0", 12 | "tslib": "^2.3.0" 13 | }, 14 | "devDependencies": { 15 | "@types/to-px": "^1.1.4" 16 | }, 17 | "sideEffects": false, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/raschidJFR/ionic-header-parallax.git" 21 | }, 22 | "keywords": [ 23 | "ionic", 24 | "parallax", 25 | "header", 26 | "ion-header" 27 | ], 28 | "author": "Raschid J.F. Rafaelly (https://github.com/RaschidJFR)", 29 | "license": "ISC", 30 | "bugs": { 31 | "url": "https://github.com/raschidJFR/ionic-header-parallax/issues" 32 | }, 33 | "homepage": "https://github.com/raschidJFR/ionic-header-parallax#readme" 34 | } 35 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/src/lib/ionic-header-parallax.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | ElementRef, 4 | Input, 5 | Renderer2, 6 | AfterContentInit, 7 | } from '@angular/core'; 8 | import toPx from 'to-px'; 9 | 10 | @Directive({ 11 | selector: 'ion-header[parallax]', 12 | standalone: true, 13 | }) 14 | export class ParallaxDirective implements AfterContentInit { 15 | @Input() imageUrl = ''; 16 | @Input() color = ''; 17 | @Input() height: string | number = 300; 18 | @Input() bgPosition: 'top' | 'center' | 'bottom' = 'top'; 19 | 20 | imageOverlay: HTMLElement | null = null; 21 | private toolbarBackground: HTMLElement | null = null; 22 | private innerScroll: HTMLElement | null = null; 23 | private originalToolbarHeight = 0; 24 | private ticking = false; 25 | private toolbarContainer: HTMLDivElement | null = null; 26 | private ionToolbar: HTMLElement | null = null; 27 | 28 | constructor( 29 | private headerRef: ElementRef, 30 | private renderer: Renderer2 31 | ) {} 32 | 33 | ngAfterContentInit() { 34 | setTimeout(() => { 35 | try { 36 | if (this.initElements()) { 37 | this.setupContentPadding(); 38 | this.setupImageOverlay(); 39 | this.setupPointerEventsForButtons(); 40 | this.setupEvents(); 41 | this.updateProgress(); 42 | } else { 43 | this.ngAfterContentInit(); 44 | } 45 | } catch (e) { 46 | console.error(e); 47 | this.ngAfterContentInit(); 48 | } 49 | }, 100); 50 | } 51 | 52 | private get header() { 53 | return this.headerRef.nativeElement; 54 | } 55 | 56 | /** 57 | * Return the value of the input parameter `height` as a string with units. 58 | * If no units were provided, it will default to 'px'. 59 | */ 60 | getMaxHeightWithUnits() { 61 | return !isNaN(+this.height) || typeof this.height === 'number' 62 | ? this.height + 'px' 63 | : this.height; 64 | } 65 | 66 | getMaxHeightInPx() { 67 | return toPx(this.getMaxHeightWithUnits()) || 0; 68 | } 69 | 70 | private initElements() { 71 | try { 72 | this.ionToolbar = this.ionToolbar || this.headerRef.nativeElement.querySelector('ion-toolbar'); 73 | if (!this.ionToolbar) { 74 | console.error('A element is needed inside '); 75 | return false; 76 | } else { 77 | this.originalToolbarHeight = this.ionToolbar.offsetHeight; 78 | this.toolbarContainer = this.ionToolbar.shadowRoot?.querySelector('.toolbar-container') || null; 79 | this.toolbarBackground = this.ionToolbar.shadowRoot?.querySelector('.toolbar-background') || null; 80 | this.color = this.color || ( 81 | this.toolbarBackground && window.getComputedStyle(this.toolbarBackground).backgroundColor 82 | ) || ''; 83 | } 84 | 85 | const parentElement = this.header?.parentElement; 86 | const ionContent = parentElement?.querySelector('ion-content'); 87 | if (!ionContent) { 88 | console.error('A element is needed'); 89 | return false; 90 | } else { 91 | this.innerScroll = ionContent.shadowRoot?.querySelector('.inner-scroll') || null; 92 | } 93 | 94 | if (!this.renderer) { 95 | return false; 96 | } else { 97 | this.renderer.setStyle(this.toolbarContainer, 'align-items', 'baseline'); 98 | } 99 | } catch (e) { 100 | console.warn(e); 101 | return false; 102 | } 103 | 104 | return true; 105 | } 106 | 107 | private setupPointerEventsForButtons() { 108 | this.renderer.setStyle(this.header, 'pointer-events', 'none'); 109 | this.ionToolbar 110 | ?.querySelectorAll('ion-buttons') 111 | .forEach(item => this.renderer.setStyle(item, 'pointer-events', 'all')); 112 | } 113 | 114 | private setupContentPadding() { 115 | const { paddingTop } = window.getComputedStyle(this.innerScroll as Element); 116 | const contentPaddingPx = toPx(paddingTop) || 0; 117 | const coverHeightPx = this.getMaxHeightInPx(); 118 | this.renderer.setStyle(this.header, 'position', 'absolute'); 119 | this.renderer.setStyle(this.innerScroll, 'padding-top', `${contentPaddingPx + coverHeightPx}px`); 120 | } 121 | 122 | private setupImageOverlay() { 123 | this.imageOverlay = this.renderer.createElement('div'); 124 | this.renderer.addClass(this.imageOverlay, 'image-overlay'); 125 | 126 | this.renderer.setStyle(this.imageOverlay, 'background-color', this.color); 127 | 128 | this.renderer.setStyle( 129 | this.imageOverlay, 130 | 'background-image', 131 | `url(${this.imageUrl || ''})` 132 | ); 133 | 134 | this.renderer.setStyle(this.imageOverlay, 'height', `100%`); 135 | this.renderer.setStyle(this.imageOverlay, 'width', '100%'); 136 | this.renderer.setStyle(this.imageOverlay, 'background-size', 'cover'); 137 | this.renderer.setStyle( 138 | this.imageOverlay, 139 | 'background-position', 140 | this.bgPosition 141 | ); 142 | 143 | this.imageOverlay && this.toolbarBackground?.appendChild(this.imageOverlay); 144 | } 145 | 146 | private setupEvents() { 147 | this.innerScroll?.addEventListener('scroll', (_event) => { 148 | if (!this.ticking) { 149 | window.requestAnimationFrame(() => { 150 | this.updateProgress(); 151 | this.ticking = false; 152 | }); 153 | } 154 | this.ticking = true; 155 | }); 156 | } 157 | 158 | /** Update the parallax effect as per the current scroll of the ion-content */ 159 | updateProgress() { 160 | const h = this.getMaxHeightInPx(); 161 | const progress = this.innerScroll && this.calcProgress(this.innerScroll, h) || 0; 162 | this.progressLayerHeight(progress); 163 | this.progressLayerOpacity(progress); 164 | } 165 | 166 | progressLayerHeight(progress: number) { 167 | const h = Math.max( 168 | this.getMaxHeightInPx() * (1 - progress), 169 | this.originalToolbarHeight 170 | ); 171 | this.renderer.setStyle(this.toolbarContainer, 'height', `${h}px`); 172 | this.renderer.setStyle(this.imageOverlay, 'height', `${h}px`); 173 | } 174 | 175 | progressLayerOpacity(progress: number) { 176 | const op = 1 - progress; 177 | this.renderer.setStyle(this.imageOverlay, 'opacity', op); 178 | } 179 | 180 | private calcProgress(scrollingElement: HTMLElement, maxHeight: number) { 181 | const scroll = +scrollingElement.scrollTop; 182 | const progress = Math.min(1, Math.max(0, scroll / maxHeight)); 183 | return progress; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/src/lib/ionic-header-parallax.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { ParallaxDirective } from './ionic-header-parallax.directive'; 3 | 4 | @NgModule({ 5 | imports: [ 6 | ParallaxDirective 7 | ], 8 | exports: [ 9 | ParallaxDirective 10 | ] 11 | }) 12 | export class IonicHeaderParallaxModule { } 13 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/src/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/ionic-header-parallax.directive'; 2 | export * from './lib/ionic-header-parallax.module'; 3 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js'; 4 | import 'zone.js/testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { 7 | BrowserDynamicTestingModule, 8 | platformBrowserDynamicTesting 9 | } from '@angular/platform-browser-dynamic/testing'; 10 | 11 | // First, initialize the Angular testing environment. 12 | getTestBed().initTestEnvironment( 13 | BrowserDynamicTestingModule, 14 | platformBrowserDynamicTesting() 15 | ); 16 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/lib", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "inlineSources": true, 10 | "types": [] 11 | }, 12 | "exclude": [ 13 | "**/*.spec.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "./tsconfig.lib.json", 5 | "compilerOptions": { 6 | "declarationMap": false 7 | }, 8 | "angularCompilerOptions": { 9 | "compilationMode": "partial" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/ionic-header-parallax/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/spec", 7 | "types": [ 8 | "jasmine" 9 | ] 10 | }, 11 | "include": [ 12 | "**/*.spec.ts", 13 | "**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaschidJFR/ionic-header-parallax/8b9aba588794641e2e67f3a6c0c20abe7e8e3925/src/app/app.component.scss -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { provideRouter } from '@angular/router'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | it('should create the app', async () => { 7 | await TestBed.configureTestingModule({ 8 | imports: [AppComponent], 9 | providers: [provideRouter([])] 10 | }).compileComponents(); 11 | 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: 'app.component.html', 7 | imports: [IonApp, IonRouterOutlet], 8 | }) 9 | export class AppComponent { 10 | constructor() {} 11 | } 12 | -------------------------------------------------------------------------------- /src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const routes: Routes = [ 4 | { 5 | path: 'home', 6 | loadComponent: () => import('./home/home.page').then((m) => m.HomePage), 7 | }, 8 | { 9 | path: '', 10 | redirectTo: 'home', 11 | pathMatch: 'full', 12 | }, 13 | ]; 14 | -------------------------------------------------------------------------------- /src/app/home/home-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { HomePage } from './home.page'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: '', 8 | component: HomePage, 9 | } 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forChild(routes)], 14 | exports: [RouterModule] 15 | }) 16 | export class HomePageRoutingModule {} 17 | -------------------------------------------------------------------------------- /src/app/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { IonicModule } from '@ionic/angular'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { RouterModule } from '@angular/router'; 6 | import { HomePage } from './home.page'; 7 | import { IonicHeaderParallaxModule } from 'ionic-header-parallax'; 8 | 9 | // This module is kept to test backward compatibility with Angular projects 10 | // that do not use standalone components. 11 | 12 | @NgModule({ 13 | declarations: [HomePage], 14 | imports: [ 15 | CommonModule, 16 | FormsModule, 17 | IonicModule, 18 | RouterModule.forChild([ 19 | { 20 | path: '', 21 | component: HomePage 22 | } 23 | ]), 24 | IonicHeaderParallaxModule 25 | ] 26 | }) 27 | export class HomePageModule {} 28 | -------------------------------------------------------------------------------- /src/app/home/home.page.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | Ionic Header Parallax 8 | 9 | 10 | 11 | 12 |
13 | Ready to create an app? 14 |

15 | Start with Ionic 16 | UI Components 22 |

23 |
24 |
25 | -------------------------------------------------------------------------------- /src/app/home/home.page.scss: -------------------------------------------------------------------------------- 1 | #container { 2 | text-align: center; 3 | 4 | position: absolute; 5 | left: 0; 6 | right: 0; 7 | top: 50%; 8 | height: 200vh; 9 | } 10 | 11 | #container strong { 12 | font-size: 20px; 13 | line-height: 26px; 14 | } 15 | 16 | #container p { 17 | font-size: 16px; 18 | line-height: 22px; 19 | 20 | color: #8c8c8c; 21 | 22 | margin: 0; 23 | } 24 | 25 | #container a { 26 | text-decoration: none; 27 | } 28 | -------------------------------------------------------------------------------- /src/app/home/home.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone'; 3 | import { ParallaxDirective } from 'ionic-header-parallax' 4 | 5 | @Component({ 6 | selector: 'app-home', 7 | templateUrl: 'home.page.html', 8 | styleUrls: ['home.page.scss'], 9 | imports: [IonHeader, IonToolbar, IonTitle, IonContent, ParallaxDirective], 10 | }) 11 | export class HomePage { 12 | constructor() {} 13 | } 14 | -------------------------------------------------------------------------------- /src/assets/icon/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaschidJFR/ionic-header-parallax/8b9aba588794641e2e67f3a6c0c20abe7e8e3925/src/assets/icon/favicon.png -------------------------------------------------------------------------------- /src/assets/shapes.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build` 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/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/global.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * App Global CSS 3 | * ---------------------------------------------------------------------------- 4 | * Put style rules here that you want to apply globally. These styles are for 5 | * the entire app and not just one component. Additionally, this file can be 6 | * used as an entry point to import other CSS/Sass files to be included in the 7 | * output CSS. 8 | * For more information on global stylesheets, visit the documentation: 9 | * https://ionicframework.com/docs/layout/global-stylesheets 10 | */ 11 | 12 | /* Core CSS required for Ionic components to work properly */ 13 | @import "@ionic/angular/css/core.css"; 14 | 15 | /* Basic CSS for apps built with Ionic */ 16 | @import "@ionic/angular/css/normalize.css"; 17 | @import "@ionic/angular/css/structure.css"; 18 | @import "@ionic/angular/css/typography.css"; 19 | @import "@ionic/angular/css/display.css"; 20 | 21 | /* Optional CSS utils that can be commented out */ 22 | @import "@ionic/angular/css/padding.css"; 23 | @import "@ionic/angular/css/float-elements.css"; 24 | @import "@ionic/angular/css/text-alignment.css"; 25 | @import "@ionic/angular/css/text-transformation.css"; 26 | @import "@ionic/angular/css/flex-utils.css"; 27 | 28 | /** 29 | * Ionic Dark Mode 30 | * ----------------------------------------------------- 31 | * For more info, please see: 32 | * https://ionicframework.com/docs/theming/dark-mode 33 | */ 34 | 35 | /* @import "@ionic/angular/css/palettes/dark.always.css"; */ 36 | /* @import "@ionic/angular/css/palettes/dark.class.css"; */ 37 | @import '@ionic/angular/css/palettes/dark.system.css'; 38 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ionic App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { RouteReuseStrategy, provideRouter, withPreloading, PreloadAllModules } from '@angular/router'; 3 | import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone'; 4 | 5 | import { routes } from './app/app.routes'; 6 | import { AppComponent } from './app/app.component'; 7 | 8 | bootstrapApplication(AppComponent, { 9 | providers: [ 10 | { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, 11 | provideIonicAngular(), 12 | provideRouter(routes, withPreloading(PreloadAllModules)), 13 | ], 14 | }); 15 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including 12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | import './zone-flags'; 46 | 47 | /*************************************************************************************************** 48 | * Zone JS is required by default for Angular itself. 49 | */ 50 | import 'zone.js'; // Included with Angular CLI. 51 | 52 | 53 | /*************************************************************************************************** 54 | * APPLICATION IMPORTS 55 | */ 56 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | // First, initialize the Angular testing environment. 11 | getTestBed().initTestEnvironment( 12 | BrowserDynamicTestingModule, 13 | platformBrowserDynamicTesting(), 14 | ); 15 | -------------------------------------------------------------------------------- /src/theme/variables.scss: -------------------------------------------------------------------------------- 1 | // For information on how to create your own theme, please see: 2 | // http://ionicframework.com/docs/theming/ 3 | -------------------------------------------------------------------------------- /src/zone-flags.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Prevents Angular change detection from 3 | * running with certain Web Component callbacks 4 | */ 5 | // eslint-disable-next-line no-underscore-dangle 6 | (window as any).__Zone_disable_customElements = true; 7 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "paths": { 12 | "ionic-header-parallax": [ 13 | "./dist/ionic-header-parallax" 14 | ] 15 | }, 16 | "noPropertyAccessFromIndexSignature": true, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "sourceMap": true, 20 | "declaration": false, 21 | "experimentalDecorators": true, 22 | "moduleResolution": "node", 23 | "importHelpers": true, 24 | "target": "es2022", 25 | "module": "es2020", 26 | "lib": ["es2018", "dom"], 27 | "useDefineForClassFields": false 28 | }, 29 | "angularCompilerOptions": { 30 | "enableI18nLegacyMessageIdFormat": false, 31 | "strictInjectionParameters": true, 32 | "strictInputAccessModifiers": true, 33 | "strictTemplates": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | --------------------------------------------------------------------------------