├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── PULL_REQUEST_TEMPLATE
│ └── pull_request_template.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── angular.json
├── browserslist
├── docs
├── COMPODOC.md
├── DEVELOPER.md
├── FEATURES.md
├── TODO.md
└── TSCOV.md
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.json
├── package-lock.json
├── package.json
├── src
├── app
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── app.routing.ts
│ ├── components
│ │ ├── backgrounds
│ │ │ ├── background-email
│ │ │ │ └── background-email.component.ts
│ │ │ ├── background-empty
│ │ │ │ └── background-empty.component.ts
│ │ │ ├── background-normal
│ │ │ │ └── background-normal.component.ts
│ │ │ ├── background-slant
│ │ │ │ └── background-slant.component.ts
│ │ │ └── backgrounds.module.ts
│ │ ├── blocks
│ │ │ ├── blocks.module.ts
│ │ │ ├── card-block
│ │ │ │ ├── card-block.component.html
│ │ │ │ ├── card-block.component.scss
│ │ │ │ └── card-block.component.ts
│ │ │ ├── first-block
│ │ │ │ ├── first-block.component.html
│ │ │ │ ├── first-block.component.scss
│ │ │ │ └── first-block.component.ts
│ │ │ ├── second-block
│ │ │ │ ├── second-block.component.html
│ │ │ │ ├── second-block.component.scss
│ │ │ │ └── second-block.component.ts
│ │ │ └── third-block
│ │ │ │ ├── third-block.component.html
│ │ │ │ ├── third-block.component.scss
│ │ │ │ └── third-block.component.ts
│ │ ├── email-me
│ │ │ ├── email-me.component.html
│ │ │ ├── email-me.component.scss
│ │ │ ├── email-me.component.spec.ts
│ │ │ └── email-me.component.ts
│ │ └── misc
│ │ │ ├── carousel
│ │ │ ├── carousel.component.html
│ │ │ ├── carousel.component.scss
│ │ │ └── carousel.component.ts
│ │ │ ├── change
│ │ │ ├── change-detail.component.ts
│ │ │ └── change-detection.component.ts
│ │ │ ├── drag-drop
│ │ │ ├── drag-drop.component.html
│ │ │ ├── drag-drop.component.scss
│ │ │ └── drag-drop.component.ts
│ │ │ ├── expansion-panel
│ │ │ ├── expansion-panel.component.html
│ │ │ ├── expansion-panel.component.scss
│ │ │ └── expansion-panel.component.ts
│ │ │ ├── misc.component.html
│ │ │ ├── misc.component.scss
│ │ │ ├── misc.component.ts
│ │ │ ├── misc.module.ts
│ │ │ ├── stepper
│ │ │ ├── stepper.component.html
│ │ │ ├── stepper.component.scss
│ │ │ └── stepper.component.ts
│ │ │ ├── table
│ │ │ ├── table.component.html
│ │ │ ├── table.component.scss
│ │ │ └── table.component.ts
│ │ │ └── virtual-reality
│ │ │ ├── virtreal.component.html
│ │ │ ├── virtreal.component.scss
│ │ │ └── virtreal.component.ts
│ ├── firebase.config.ts
│ ├── pages
│ │ ├── about-me
│ │ │ ├── about-me.component.html
│ │ │ ├── about-me.component.spec.ts
│ │ │ └── about-me.component.ts
│ │ ├── auth
│ │ │ ├── auth.component.html
│ │ │ ├── auth.component.scss
│ │ │ ├── auth.component.ts
│ │ │ ├── auth.module.ts
│ │ │ └── phone-signin
│ │ │ │ ├── phone-signin.component.html
│ │ │ │ ├── phone-signin.component.scss
│ │ │ │ ├── phone-signin.component.spec.ts
│ │ │ │ └── phone-signin.component.ts
│ │ ├── contact
│ │ │ ├── contact.component.html
│ │ │ ├── contact.component.scss
│ │ │ ├── contact.component.spec.ts
│ │ │ └── contact.component.ts
│ │ ├── home
│ │ │ ├── home.component.html
│ │ │ └── home.component.ts
│ │ ├── not-found
│ │ │ └── not-found.component.ts
│ │ └── profile
│ │ │ ├── profile-settings.component.html
│ │ │ ├── profile-settings.component.scss
│ │ │ ├── profile-settings.component.ts
│ │ │ ├── profile.component.html
│ │ │ ├── profile.component.scss
│ │ │ ├── profile.component.ts
│ │ │ └── profile.module.ts
│ └── shared
│ │ ├── index.ts
│ │ ├── interfaces
│ │ ├── image.interface.ts
│ │ ├── index.ts
│ │ └── scene.interface.ts
│ │ ├── layout
│ │ ├── footer
│ │ │ ├── footer.component.html
│ │ │ ├── footer.component.scss
│ │ │ └── footer.component.ts
│ │ ├── header
│ │ │ ├── header.component.html
│ │ │ ├── header.component.scss
│ │ │ └── header.component.ts
│ │ └── index.ts
│ │ ├── models
│ │ ├── contact.model.ts
│ │ ├── index.ts
│ │ ├── phone-number.model.ts
│ │ ├── profile.model.ts
│ │ └── user.model.ts
│ │ ├── pipes
│ │ ├── async-observable-pipe.component.ts
│ │ ├── currency-pipe.component.ts
│ │ ├── date-pipe.component.ts
│ │ ├── json-pipe.component.ts
│ │ ├── pipes.module.ts
│ │ ├── titlecase-pipe.component.ts
│ │ └── year-pipe.component.ts
│ │ ├── services
│ │ ├── alert.service.ts
│ │ ├── auth-guard.service.ts
│ │ ├── auth.service.ts
│ │ ├── index.ts
│ │ ├── user.service.ts
│ │ └── window.service.ts
│ │ └── validators
│ │ ├── email.ts
│ │ └── index.ts
├── assets
│ ├── .gitkeep
│ ├── css
│ │ └── bootstrap-social.css
│ └── img
│ │ ├── angular.png
│ │ ├── angular2.png
│ │ ├── mb-bg-01.png
│ │ ├── mb-bg-02.png
│ │ ├── mb-bg-03.png
│ │ ├── mb-bg-04.png
│ │ ├── mb-bg-05.png
│ │ ├── mb-bg-06.png
│ │ └── mountsthelens.png
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── karma.conf.js
├── main.ts
├── polyfills.ts
├── styles.scss
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── tslint.json
├── tsconfig.json
├── tslint.json
└── typings.json
/.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 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md:
--------------------------------------------------------------------------------
1 | * **Please check if the PR fulfills these requirements**
2 | - [ ] The commit message follows our guidelines
3 | - [ ] Tests for the changes have been added (for bug fixes / features)
4 | - [ ] Docs have been added / updated (for bug fixes / features)
5 |
6 |
7 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
8 |
9 |
10 |
11 | * **What is the current behavior?** (You can also link to an open issue here)
12 |
13 |
14 |
15 | * **What is the new behavior (if this is a feature change)?**
16 |
17 |
18 |
19 | * **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
20 |
21 |
22 |
23 | * **Other information**:
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 |
10 | # compodoc documentation
11 | /documentation
12 |
13 | # IDEs and editors
14 | /.idea
15 | .project
16 | .classpath
17 | .c9/
18 | *.launch
19 | .settings/
20 |
21 | # IDE - VSCode
22 | .vscode
23 | .vscode/*
24 | !.vscode/settings.json
25 | !.vscode/tasks.json
26 | !.vscode/launch.json
27 | !.vscode/extensions.json
28 |
29 | # misc
30 | /.sass-cache
31 | /connect.lock
32 | /coverage/*
33 | /libpeerconnection.log
34 | npm-debug.log
35 | testem.log
36 | /typings
37 | package-lock.json
38 |
39 | # e2e
40 | /e2e/*.js
41 | /e2e/*.map
42 |
43 | #System Files
44 | .DS_Store
45 | Thumbs.db
46 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jeroenouw@outlook.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 - 2019 jeroenouw
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://ngxmatfire.jerouw.nl/)
2 | []()
3 | [](https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/LICENSE/)
4 | [](https://github.com/jeroenouw/AngularMaterialFirebase/stargazers)
5 | []()
6 |
7 | 
8 |
9 | # Angular 8 | Material Design | Firebase - Starter kit
10 |
11 | A full stack starter app containing [Angular 8](https://angular.io), [Material](https://material.io/) and [Firebase](https://firebase.google.com/). See also: [AngularMaterialGo](https://github.com/jeroenouw/AngularMaterialGo)
12 |
13 | ## [Live demo](http://ngxmatfire.jerouw.nl)
14 |
15 | 
16 |
17 | ## Quick start, Development & Production
18 |
19 | [Get started now](https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/docs/DEVELOPER.md)
20 |
21 | ## All features
22 |
23 | [Discover all the features in this project](https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/docs/FEATURES.md)
24 |
25 | ## [Live automated documentation](http://ngxmatfire-docs.jerouw.nl)
26 |
27 | [Generate technical documentation automatically with Compodoc](https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/docs/COMPODOC.md)
28 |
29 | ## Check your type coverage
30 |
31 | [See your type coverage with tscov](https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/docs/TSCOV.md)
32 |
33 | ## Future updates
34 |
35 | [To do and finish list](https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/docs/TODO.md)
36 |
37 | ## Contributing
38 |
39 | Want to file a bug, contribute some code, or improve documentation? Feel free to place an [issue](https://github.com/jeroenouw/AngularMaterialFirebase/issues).
40 |
41 | ## License
42 |
43 | [](https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/LICENSE/)
44 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "ngxmatfire": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {},
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "dist/ngxmatfire",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "tsConfig": "src/tsconfig.app.json",
20 | "polyfills": "src/polyfills.ts",
21 | "assets": [
22 | "src/assets",
23 | "src/favicon.ico",
24 | {
25 | "glob": "**/*",
26 | "input": "node_modules/ngx-auth-firebaseui/assets/",
27 | "output": "./assets/"
28 | }
29 | ],
30 | "styles": [
31 | "src/styles.scss"
32 | ],
33 | "scripts": []
34 | },
35 | "configurations": {
36 | "production": {
37 | "optimization": true,
38 | "outputHashing": "all",
39 | "sourceMap": false,
40 | "extractCss": true,
41 | "namedChunks": false,
42 | "aot": true,
43 | "extractLicenses": true,
44 | "vendorChunk": false,
45 | "buildOptimizer": true,
46 | "fileReplacements": [
47 | {
48 | "replace": "src/environments/environment.ts",
49 | "with": "src/environments/environment.prod.ts"
50 | }
51 | ]
52 | }
53 | }
54 | },
55 | "serve": {
56 | "builder": "@angular-devkit/build-angular:dev-server",
57 | "options": {
58 | "browserTarget": "ngxmatfire:build"
59 | },
60 | "configurations": {
61 | "production": {
62 | "browserTarget": "ngxmatfire:build:production"
63 | }
64 | }
65 | },
66 | "extract-i18n": {
67 | "builder": "@angular-devkit/build-angular:extract-i18n",
68 | "options": {
69 | "browserTarget": "ngxmatfire:build"
70 | }
71 | },
72 | "test": {
73 | "builder": "@angular-devkit/build-angular:karma",
74 | "options": {
75 | "main": "src/test.ts",
76 | "karmaConfig": "src/karma.conf.js",
77 | "polyfills": "src/polyfills.ts",
78 | "tsConfig": "src/tsconfig.spec.json",
79 | "scripts": [],
80 | "styles": [
81 | "src/styles.scss"
82 | ],
83 | "assets": [
84 | "src/assets",
85 | "src/favicon.ico"
86 | ]
87 | }
88 | },
89 | "lint": {
90 | "builder": "@angular-devkit/build-angular:tslint",
91 | "options": {
92 | "tsConfig": [
93 | "src/tsconfig.app.json",
94 | "src/tsconfig.spec.json"
95 | ],
96 | "exclude": []
97 | }
98 | }
99 | }
100 | },
101 | "ngxmatfire-e2e": {
102 | "root": "e2e/",
103 | "projectType": "application",
104 | "architect": {
105 | "e2e": {
106 | "builder": "@angular-devkit/build-angular:protractor",
107 | "options": {
108 | "protractorConfig": "e2e/protractor.conf.js",
109 | "devServerTarget": "ngxmatfire:serve"
110 | }
111 | },
112 | "lint": {
113 | "builder": "@angular-devkit/build-angular:tslint",
114 | "options": {
115 | "tsConfig": "e2e/tsconfig.e2e.json",
116 | "exclude": [
117 | "**/node_modules/**"
118 | ]
119 | }
120 | }
121 | }
122 | }
123 | },
124 | "defaultProject": "ngxmatfire"
125 | }
126 |
--------------------------------------------------------------------------------
/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed
5 | > 0.5%
6 | last 2 versions
7 | Firefox ESR
8 | not dead
9 | # IE 9-11
10 |
--------------------------------------------------------------------------------
/docs/COMPODOC.md:
--------------------------------------------------------------------------------
1 | # Compodoc automated documentation
2 |
3 | [Compodoc](https://compodoc.github.io/website/) is a documentation tool for Angular applications. It generates a static documentation of your application. Others developers of your team, or internet visitors for a public documentation, can easily understand the features of your application or library.
4 |
5 | ## How to run
6 |
7 | Generate documentation.
8 |
9 | ```cmd
10 | npm run compodoc:g
11 | ```
12 |
13 | Generate documentation and serve on `http://localhost:8080`.
14 |
15 | ```cmd
16 | npm run compodoc:s
17 | ```
18 |
19 | Generate documentation, serve on `http://localhost:8080` and watch changes.
20 |
21 | ```cmd
22 | npm run compodoc:w
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/DEVELOPER.md:
--------------------------------------------------------------------------------
1 | # DEVELOPER
2 |
3 | ## Quick start
4 |
5 | First clone this repo: `git clone https://github.com/jeroenouw/AngularMaterialFirebase.git`.
6 | Change directory to this project
7 | Run `npm install` to install all the dependencies.
8 | Run `npm start` to run this project. This will run with the AoT Compiler.
9 | Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
10 | Run `npm reset` if you want to re-install everything.
11 |
12 | ## Development
13 |
14 | For own projects please use different keys in `src/app/firebase.config.ts`:
15 |
16 | ```typescript
17 |
18 | export const firebaseKeys = {
19 | // For your own projects please use different keys
20 | apiKey: 'YOUR_KEY',
21 | authDomain: 'YOUR_DOMAIN',
22 | databaseURL: 'YOUR_URL',
23 | projectId: 'YOUR_ID',
24 | storageBucket: 'YOUR_KEY',
25 | messagingSenderId: 'YOUR_ID'
26 | }
27 |
28 | ```
29 |
30 | Run `npm start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
31 |
32 | To build the development environment, run `npm run dist`.
33 |
34 | ## Production
35 |
36 | To build the production environment, run `npm run prod`. This will run with the AoT Compiler.
37 | To build the production environment without hashing in the files, run `npm run prod:hashless`. This will give packages without a hash.
38 | Also available: run `npm run prod:src`.
39 |
--------------------------------------------------------------------------------
/docs/FEATURES.md:
--------------------------------------------------------------------------------
1 | # FEATURES
2 |
3 | ## Features in this project
4 |
5 | * Angular 8 front-end
6 | * Material Design
7 | * Firebase back-end (OAuth authentication and NoSQL database)
8 | * Automated documentation (compodoc)
9 | * Home page
10 | * About page
11 | * Contact page
12 | * Misc page
13 | * 404 page
14 | * Profile page
15 | * Profile settings page
16 | * Login page
17 | * Signup page
18 | * Save email (keep in touch)
19 | * ngx-auth-firebaseui for authentication (Anonymous, Email, Google, Twitter, Facebook and Github)
20 | * Verification email after signup
21 | * Auth Guard
22 | * Profile image animation
23 | * Password reset
24 | * Alerts
25 | * 360 view
26 | * Multiple pipes
27 | * Various components to use
28 | * Carousel
29 | * Back to top button
30 | * Loading spinner and/or progress bar
31 |
32 | ## Material Design usage in this project
33 |
34 | * Datepicker
35 | * Input
36 | * Slide toggle
37 | * Menu
38 | * Sidenav
39 | * Toolbar
40 | * Table
41 | * Stepper
42 | * Expansion panel
43 | * Card
44 | * Tabs
45 | * Buttons
46 | * Icon
47 | * Progress spinner
48 | * Progress bar
49 | * Tooltip
50 | * Snackbar
51 | * Drag & drop
52 |
--------------------------------------------------------------------------------
/docs/TODO.md:
--------------------------------------------------------------------------------
1 | # TODO
2 |
3 | All improvements to this repo will first be done in a sub branch.
4 |
5 | ## To Do List
6 |
7 | * Make every component responsive (@angular/flex-layout?)
8 | * Messaging
9 |
10 | ## To Finish List (known issues)
11 |
12 | ### Auth
13 |
14 | * Cellphone authentication (optimizing)
15 | * Session
16 | * Google, Facebook, Twitter & Github signin/signup
17 |
18 | ### Profile
19 |
20 | * Profile image uploading
21 |
22 | ### Tests
23 |
24 | * Unit
25 | * e2e
26 |
27 | ### Other
28 |
29 | * 360 view only working on localhost
30 |
--------------------------------------------------------------------------------
/docs/TSCOV.md:
--------------------------------------------------------------------------------
1 | # liftr-tscov automated type coverage
2 |
3 | [tscov](https://github.com/jeroenouw/liftr-tscov) checks the type coverage of any TypeScript project.
4 |
5 | ## How to run
6 |
7 | See output type coverage (minimum coverage is set by 90%)
8 |
9 | ```cmd
10 | npm run tscov
11 | ```
12 |
13 | See output type coverage with details
14 |
15 | ```cmd
16 | npm run tscov:d
17 | ```
18 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { Angular4ProjectPage } from './app.po';
2 | import { browser, element, by } from 'protractor';
3 |
4 | describe('angular4-project App', function() {
5 | let page: Angular4ProjectPage;
6 |
7 | beforeEach(() => {
8 | page = new Angular4ProjectPage();
9 | });
10 |
11 | /*it('should display message saying app works', () => {
12 | page.navigateTo();
13 | expect(page.getParagraphText()).toEqual('app works!');
14 | });
15 |
16 | it('should display message lorem ipsum dolor...', () => {
17 | page.navigateTo();
18 | expect(page.getParagraphText()).toEqual('Lorem ipsum dolor');
19 | });*/
20 |
21 | it('Button text should be register', () => {
22 | browser.get('http://localhost:4200/signup');
23 | expect(browser.getButtonText()).toEqual('Register');
24 | });
25 |
26 | });
27 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, element, by } from 'protractor';
2 |
3 | export class Angular4ProjectPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getHeadingText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 |
12 | getParagraphText() {
13 | return element(by.css('app-first-block p')).getText();
14 | }
15 |
16 | getButtonText() {
17 | return element(by.buttonText('Register')).getText();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/e2e/tsconfig.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 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngxmatfire",
3 | "version": "1.5.1",
4 | "author": "Jeroen Ouwehand",
5 | "description": "Full stack starter app with Angular 8, Material Design and Firebase.",
6 | "keywords": [
7 | "angular",
8 | "angular 8",
9 | "firebase",
10 | "material",
11 | "nosql",
12 | "oauth",
13 | "starter-template",
14 | "ngx"
15 | ],
16 | "repository": "https://github.com/jeroenouw/AngularMaterialFirebase",
17 | "license": "MIT",
18 | "angular-cli": {},
19 | "scripts": {
20 | "ng": "ng",
21 | "start": "ng serve",
22 | "test": "ng test",
23 | "pree2e": "webdriver-manager update --standalone false --gecko false",
24 | "e2e": "ng e2e",
25 | "lint": "ng lint",
26 | "reset": "rm -rf ./node_modules ./package-lock.json && npm install",
27 | "dist": "ng build",
28 | "prod": "ng build --prod",
29 | "prod:hashless": "ng build --prod --output-hashing none",
30 | "prod:src": "ng build --prod --sourcemaps",
31 | "compodoc:g": "compodoc -p src/tsconfig.app.json",
32 | "compodoc:s": "compodoc -p src/tsconfig.app.json --serve",
33 | "compodoc:w": "compodoc -p src/tsconfig.app.json --serve --watch",
34 | "tscov": "tscov -m 90",
35 | "tscov:d": "tscov --details",
36 | "link-upstream": "git remote add upstream https://github.com/jeroenouw/AngularMaterialFirebase",
37 | "sync": "git fetch && git checkout master && git merge upstream/master && git push"
38 | },
39 | "dependencies": {
40 | "@angular/animations": "^8.0.0",
41 | "@angular/cdk": "^8.0.0",
42 | "@angular/common": "^8.0.0",
43 | "@angular/compiler": "^8.0.0",
44 | "@angular/core": "^8.0.0",
45 | "@angular/fire": "^5.1.3",
46 | "@angular/flex-layout": "^8.0.0-beta.26",
47 | "@angular/forms": "^8.0.0",
48 | "@angular/material": "^8.0.0",
49 | "@angular/platform-browser": "^8.0.0",
50 | "@angular/platform-browser-dynamic": "^8.0.0",
51 | "@angular/platform-server": "^8.0.0",
52 | "@angular/router": "^8.0.0",
53 | "core-js": "^3.1.3",
54 | "firebase": "^5.7.3",
55 | "hammerjs": "^2.0.8",
56 | "ngx-auth-firebaseui": "^2.2.1",
57 | "rxjs": "^6.5.0",
58 | "rxjs-compat": "^6.5.0",
59 | "ts-helpers": "*",
60 | "zone.js": "~0.9.1"
61 | },
62 | "devDependencies": {
63 | "@angular-devkit/build-angular": "~0.800.0",
64 | "@angular/cli": "^8.0.0",
65 | "@angular/compiler-cli": "^8.0.0",
66 | "@angular/language-service": "^8.0.0",
67 | "@compodoc/compodoc": "^1.1.11",
68 | "@liftr/tscov": "^1.4.1",
69 | "@types/jasmine": "^3.3.9",
70 | "@types/node": "^11.10.4",
71 | "codelyzer": "^5.0.1",
72 | "jasmine-core": "^3.3.0",
73 | "jasmine-spec-reporter": "^4.2.1",
74 | "karma": "^4.0.1",
75 | "karma-chrome-launcher": "^2.2.0",
76 | "karma-cli": "^2.0.0",
77 | "karma-coverage-istanbul-reporter": "^2.0.4",
78 | "karma-jasmine": "^2.0.1",
79 | "karma-jasmine-html-reporter": "^1.4.0",
80 | "protractor": "^5.4.2",
81 | "rxjs-tslint": "^0.1.7",
82 | "ts-node": "^7.0.1",
83 | "tslint": "^5.13.1",
84 | "webpack": "^4.29.6"
85 | },
86 | "peerDependencies": {
87 | "typescript": "3.4.5"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | .app-content {
2 | padding: 20px;
3 | }
4 |
5 | .app-content mat-card{
6 | margin: 20px;
7 | }
8 |
9 | .container {
10 | z-index: 20;
11 | display: inherit;
12 | padding: 0;
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { TestBed, async } from '@angular/core/testing';
4 | import { AppComponent } from './app.component';
5 |
6 | describe('AppComponent', () => {
7 | beforeEach(() => {
8 | TestBed.configureTestingModule({
9 | declarations: [
10 | AppComponent
11 | ],
12 | });
13 | TestBed.compileComponents();
14 | });
15 |
16 | it('should create the app', async(() => {
17 | const fixture = TestBed.createComponent(AppComponent);
18 | const app = fixture.debugElement.componentInstance;
19 | expect(app).toBeTruthy();
20 | }));
21 |
22 | it(`should have as title 'app works!'`, async(() => {
23 | const fixture = TestBed.createComponent(AppComponent);
24 | const app = fixture.debugElement.componentInstance;
25 | expect(app.title).toEqual('app works!');
26 | }));
27 |
28 | it('should render title in a h1 tag', async(() => {
29 | const fixture = TestBed.createComponent(AppComponent);
30 | fixture.detectChanges();
31 | const compiled = fixture.debugElement.nativeElement;
32 | expect(compiled.querySelector('h1').textContent).toContain('app works!');
33 | }));
34 | });
35 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import * as firebase from 'firebase';
3 | import { firebaseKeys } from './firebase.config';
4 |
5 | @Component({
6 | selector: 'app-root',
7 | templateUrl: './app.component.html',
8 | styleUrls: ['./app.component.scss']
9 | })
10 |
11 | export class AppComponent implements OnInit {
12 |
13 | public ngOnInit(): void {
14 | firebase.initializeApp(firebaseKeys);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | // Modules 3rd party
2 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
3 | import { BrowserModule } from '@angular/platform-browser';
4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
5 | import { MatButtonModule, MatCheckboxModule, MatMenuModule, MatInputModule, MatSnackBarModule,
6 | MatToolbarModule, MatDialogModule, MatSidenavModule, MatNativeDateModule,
7 | MatCardModule, MatTabsModule, MatIconModule } from '@angular/material';
8 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
9 | import { HttpClientModule } from '@angular/common/http';
10 | import { NgxAuthFirebaseUIModule } from 'ngx-auth-firebaseui';
11 |
12 | // Modules
13 | import { BlocksModule } from './components/blocks/blocks.module';
14 | import { AuthModule } from './pages/auth/auth.module';
15 | import { BackgroundsModule } from './components/backgrounds/backgrounds.module';
16 | import { ProfileModule } from './pages/profile/profile.module';
17 | import { MiscModule } from './components/misc/misc.module';
18 | import { PipesModule } from '@shared/pipes/pipes.module';
19 |
20 | // Shared
21 | import {
22 | FooterComponent,
23 | HeaderComponent,
24 | UserService,
25 | AlertService,
26 | AuthGuardService,
27 | AuthService,
28 | WindowService
29 | } from '@shared';
30 |
31 | // Main
32 | import { AppComponent } from './app.component';
33 | import { AppRoutingModule } from './app.routing';
34 | import { firebaseKeys } from './firebase.config';
35 |
36 | // Pages
37 | import { HomeComponent } from './pages/home/home.component';
38 | import { AboutMeComponent } from './pages/about-me/about-me.component';
39 | import { ContactComponent } from './pages/contact/contact.component';
40 | import { PageNotFoundComponent } from './pages/not-found/not-found.component';
41 |
42 | // Components
43 | import { EmailMeComponent } from './components/email-me/email-me.component';
44 |
45 | @NgModule({
46 | declarations: [
47 | AppComponent,
48 | HomeComponent,
49 | AboutMeComponent,
50 | ContactComponent,
51 | HeaderComponent,
52 | FooterComponent,
53 | PageNotFoundComponent,
54 | EmailMeComponent
55 | ],
56 | imports: [
57 | BrowserModule,
58 | BrowserAnimationsModule,
59 | MatButtonModule, MatCheckboxModule, MatMenuModule, MatInputModule, MatSnackBarModule,
60 | MatToolbarModule, MatDialogModule, MatSidenavModule, MatNativeDateModule,
61 | MatCardModule, MatTabsModule, MatIconModule,
62 | FormsModule,
63 | ReactiveFormsModule,
64 | HttpClientModule,
65 | AppRoutingModule,
66 | PipesModule,
67 | BlocksModule,
68 | AuthModule,
69 | BackgroundsModule,
70 | ProfileModule,
71 | MiscModule,
72 | NgxAuthFirebaseUIModule.forRoot(firebaseKeys)
73 | ],
74 | providers: [
75 | UserService,
76 | AlertService,
77 | AuthGuardService,
78 | AuthService,
79 | WindowService
80 | ],
81 | schemas: [
82 | CUSTOM_ELEMENTS_SCHEMA
83 | ],
84 | bootstrap: [AppComponent]
85 | })
86 | export class AppModule {
87 | }
88 |
--------------------------------------------------------------------------------
/src/app/app.routing.ts:
--------------------------------------------------------------------------------
1 | // Modules 3rd party
2 | import { NgModule } from '@angular/core';
3 | import { RouterModule, Routes } from '@angular/router';
4 |
5 | // 404 page
6 | import { PageNotFoundComponent } from './pages/not-found/not-found.component';
7 |
8 | // Pages
9 | import { HomeComponent } from './pages/home/home.component';
10 | import { AboutMeComponent } from './pages/about-me/about-me.component';
11 | import { ContactComponent } from './pages/contact/contact.component';
12 | import { AuthComponent } from './pages/auth/auth.component';
13 | import { ProfileComponent } from './pages/profile/profile.component';
14 | import { ProfileSettingsComponent } from './pages/profile/profile-settings.component';
15 |
16 | // Components
17 | import { MiscComponent } from './components/misc/misc.component';
18 |
19 | // Protected
20 | import { AuthGuardService } from '@shared';
21 |
22 | // Routing
23 | const appRoutes: Routes = [
24 |
25 | // Public pages
26 | { path: '', redirectTo: '/home', pathMatch : 'full' },
27 | { path: 'home', component: HomeComponent },
28 | { path: 'about', component: AboutMeComponent },
29 | { path: 'contact', component: ContactComponent },
30 | { path: 'misc', component: MiscComponent },
31 | { path: 'auth', component: AuthComponent },
32 |
33 | // Protected pages
34 | // { path: 'profile/:uid/:name', component: ProfileComponent, canActivate: [AuthGuardService] },
35 | { path: 'profile', component: ProfileComponent, canActivate: [AuthGuardService] },
36 | { path: 'profile-settings', component: ProfileSettingsComponent, canActivate: [AuthGuardService] },
37 | { path: '**', component: PageNotFoundComponent }
38 | ];
39 |
40 | @NgModule({
41 | imports: [RouterModule.forRoot(appRoutes)],
42 | exports: [RouterModule]
43 | })
44 |
45 | export class AppRoutingModule {
46 | }
47 |
--------------------------------------------------------------------------------
/src/app/components/backgrounds/background-email/background-email.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-background-email',
5 | template: `
6 |
7 |
8 |
9 | `,
10 | styles: [`
11 | .background img {
12 | position: relative;
13 | width: 100%;
14 | max-height: 500px;
15 | margin: 0 auto;
16 | z-index: 1;
17 | box-shadow: 2px 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12);
18 | }
19 |
20 | @media only screen and (max-width: 1024px) {
21 | .background img {
22 | width: 100%;
23 | height: 270px;
24 | }
25 | }
26 | `]
27 | })
28 | export class BackgroundEmailComponent {
29 | public fullImagePath: string = '/assets/img/mb-bg-01.png';
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/components/backgrounds/background-empty/background-empty.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-background-empty',
5 | template: `
`,
6 | styles: [`
7 | .background {
8 | position: relative;
9 | width: 100%;
10 | height: 500px;
11 | z-index: 1;
12 | }`]
13 | })
14 | export class BackgroundEmptyComponent {}
15 |
--------------------------------------------------------------------------------
/src/app/components/backgrounds/background-normal/background-normal.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-background-normal',
5 | template: `
6 |
7 |
8 |
9 | `,
10 | styles: [
11 | `.background img {
12 | position: relative;
13 | width: 100%;
14 | max-height: 300px;
15 | margin: 0 auto;
16 | z-index: 1;
17 | box-shadow: 2px 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12);
18 | }`
19 | ]
20 | })
21 | export class BackgroundNormalComponent {
22 | public fullImagePath: string = '/assets/img/mb-bg-06.png';
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/components/backgrounds/background-slant/background-slant.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-background-slant',
5 | template: `
`,
6 | styles: [
7 | `.background-slant {
8 | position: relative;
9 | overflow: hidden;
10 | background-color: #673ab7;
11 | transform: skew(0,-4deg);
12 | -webkit-transform: skew(0,-4deg);
13 | -ms-transform: skew(0,-4deg);
14 | left: 0px;
15 | right: 0px;
16 | height: 520px;
17 | z-index: 1;
18 | }
19 | `]
20 | })
21 | export class BackgroundSlantComponent {}
22 |
--------------------------------------------------------------------------------
/src/app/components/backgrounds/backgrounds.module.ts:
--------------------------------------------------------------------------------
1 | // Modules 3rd party
2 | import { NgModule } from '@angular/core';
3 | import { CommonModule } from '@angular/common';
4 |
5 | // Components
6 | import { BackgroundNormalComponent } from './background-normal/background-normal.component';
7 | import { BackgroundSlantComponent } from './background-slant/background-slant.component';
8 | import { BackgroundEmptyComponent } from './background-empty/background-empty.component';
9 | import { BackgroundEmailComponent } from './background-email/background-email.component';
10 |
11 | @NgModule({
12 | declarations: [
13 | BackgroundNormalComponent,
14 | BackgroundSlantComponent,
15 | BackgroundEmptyComponent,
16 | BackgroundEmailComponent
17 | ],
18 | imports: [
19 | CommonModule
20 | ],
21 | exports: [
22 | BackgroundNormalComponent,
23 | BackgroundSlantComponent,
24 | BackgroundEmptyComponent,
25 | BackgroundEmailComponent
26 | ]
27 | })
28 | export class BackgroundsModule {
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/components/blocks/blocks.module.ts:
--------------------------------------------------------------------------------
1 | // Modules 3rd party
2 | import { NgModule } from '@angular/core';
3 | import { CommonModule } from '@angular/common';
4 | import { MatButtonModule, MatCardModule } from '@angular/material';
5 |
6 | // Components
7 | import { CardBlockComponent } from './card-block/card-block.component';
8 | import { FirstBlockComponent } from './first-block/first-block.component';
9 | import { SecondBlockComponent } from './second-block/second-block.component';
10 | import { ThirdBlockComponent } from './third-block/third-block.component';
11 |
12 | @NgModule({
13 | declarations: [
14 | CardBlockComponent,
15 | FirstBlockComponent,
16 | SecondBlockComponent,
17 | ThirdBlockComponent
18 | ],
19 | imports: [
20 | CommonModule,
21 | MatButtonModule, MatCardModule
22 | ],
23 | exports: [
24 | CardBlockComponent,
25 | FirstBlockComponent,
26 | SecondBlockComponent,
27 | ThirdBlockComponent
28 | ]
29 | })
30 | export class BlocksModule {
31 | }
32 |
--------------------------------------------------------------------------------
/src/app/components/blocks/card-block/card-block.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{titleCard}} {{numberCard}}
4 | {{subTitleCard}}
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{contentCard}}
12 |
13 |
14 |
15 |
16 |
17 |
18 | Fork on Github
19 |
20 |
--------------------------------------------------------------------------------
/src/app/components/blocks/card-block/card-block.component.scss:
--------------------------------------------------------------------------------
1 | .contentCard {
2 | width: 890px;
3 | height: 500px;
4 | position: absolute;
5 | padding: 24px;
6 | margin: 10px auto 0;
7 | left: 0;
8 | right: 0;
9 | z-index: 5;
10 | }
11 |
12 | .contentCard img {
13 | margin: 0 33px;
14 | max-width: 210px;
15 | max-height: 250px;
16 | }
17 |
18 | .buttonCard {
19 | display: grid;
20 | margin: 0;
21 | }
22 |
23 | .buttonCard a {
24 | text-decoration: none;
25 | }
26 |
27 | @media only screen and (max-width: 1024px) {
28 |
29 | .contentCard {
30 | width: 100%;
31 | height: 100%;
32 | }
33 |
34 | .contentCard img {
35 | margin: 0 10px;
36 | max-width: 30%;
37 | max-height: 80%;
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/src/app/components/blocks/card-block/card-block.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-card-block',
5 | templateUrl: './card-block.component.html',
6 | styleUrls: ['./card-block.component.scss']
7 | })
8 | export class CardBlockComponent {
9 | titleCard = 'Angular';
10 | numberCard = 8;
11 | subTitleCard = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi nisl ligula.';
12 | contentCard = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi nisl ligula.';
13 | angularImage: string;
14 |
15 | constructor() {
16 | this.angularImage = '/assets/img/angular2.png'; }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/components/blocks/first-block/first-block.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{titleOne}}
4 | {{contentOne}}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/app/components/blocks/first-block/first-block.component.scss:
--------------------------------------------------------------------------------
1 | #contentOne {
2 | width: 930px;
3 | margin: 0 auto;
4 | left: 0;
5 | right: 0;
6 | padding: 60px 20px;
7 | z-index: 1;
8 | position: relative;
9 | }
10 |
11 | @media only screen and (max-width: 1024px) {
12 |
13 | #contentOne {
14 | width: 100%;
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/src/app/components/blocks/first-block/first-block.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-first-block',
5 | templateUrl: './first-block.component.html',
6 | styleUrls: ['./first-block.component.scss']
7 | })
8 | export class FirstBlockComponent {
9 | titleOne = 'Starter kit';
10 | contentOne = 'Angular 8 | Material Design | Firebase (OAuth authentication and NoSQL database)';
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/components/blocks/second-block/second-block.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{titleTwo}}
4 | {{contentTwo}}
5 |
6 |
--------------------------------------------------------------------------------
/src/app/components/blocks/second-block/second-block.component.scss:
--------------------------------------------------------------------------------
1 | #contentTwo{
2 | width: 930px;
3 | margin: 0 auto;
4 | left: 0;
5 | right: 0;
6 | padding: 120px 20px 120px;
7 | z-index: 3;
8 | position: relative;
9 | }
10 |
11 | @media only screen and (max-width: 1024px) {
12 |
13 | #contentTwo {
14 | width: 100%;
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/src/app/components/blocks/second-block/second-block.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-second-block',
5 | templateUrl: './second-block.component.html',
6 | styleUrls: ['./second-block.component.scss']
7 | })
8 | export class SecondBlockComponent {
9 | titleTwo = 'Authentication and Database';
10 | contentTwo = 'Provided by Firebase';
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/components/blocks/third-block/third-block.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{titleThree}}
4 | {{contentThree}}
5 |
6 |
7 | {{titleFour}}
8 | {{contentFour}}
9 |
10 |
11 | {{titleFive}}
12 | {{contentFive}}
13 |
14 |
--------------------------------------------------------------------------------
/src/app/components/blocks/third-block/third-block.component.scss:
--------------------------------------------------------------------------------
1 | #contentThree {
2 | width: 930px;
3 | margin: 0 auto;
4 | left: 0;
5 | right: 0;
6 | padding: 120px 20px 120px;
7 | z-index: 3;
8 | position: relative;
9 | }
10 |
11 | #contentThree .mat-card {
12 | width: 30%;
13 | margin: 10px;
14 | display: inline-block;
15 | }
16 |
17 | @media only screen and (max-width: 1024px) {
18 |
19 | #contentThree, #contentThree .mat-card {
20 | width: 100%;
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/src/app/components/blocks/third-block/third-block.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-third-block',
5 | templateUrl: './third-block.component.html',
6 | styleUrls: ['./third-block.component.scss']
7 | })
8 | export class ThirdBlockComponent {
9 | titleThree = 'Angular 8';
10 | contentThree = 'Angular is a framework for building client applications in HTML and TypeScript that compiles to JavaScript.';
11 |
12 | titleFour = 'Material 2';
13 | contentFour = 'Material Design is a unified system that combines theory, resources, and tools for crafting digital experiences.';
14 |
15 | titleFive = 'Firebase';
16 | contentFive = 'Firebase is built on Google infrastructure and scales automatically, for even the largest apps.';
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/components/email-me/email-me.component.html:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/app/components/email-me/email-me.component.scss:
--------------------------------------------------------------------------------
1 | .form {
2 | width: 500px;
3 | margin: 165px auto 0;
4 | position: absolute;
5 | left: 0;
6 | right: 0;
7 | z-index: 5;
8 | }
9 |
10 | .full-width {
11 | width: 100%;
12 | }
13 |
14 | @media only screen and (max-width: 1024px) {
15 |
16 | .form {
17 | width: 50%;
18 | margin: 10% auto 0;
19 | }
20 |
21 | }
22 |
23 | @media only screen and (max-width: 768px) {
24 |
25 | .form {
26 | width: 90%;
27 | margin: 10% auto;
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/src/app/components/email-me/email-me.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { EmailMeComponent } from './email-me.component';
7 |
8 | describe('EmailMeComponent', () => {
9 | let component: EmailMeComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ EmailMeComponent ]
15 | })
16 | .compileComponents();
17 | }));
18 |
19 | beforeEach(() => {
20 | fixture = TestBed.createComponent(EmailMeComponent);
21 | component = fixture.componentInstance;
22 | fixture.detectChanges();
23 | });
24 |
25 | it('should create', () => {
26 | expect(component).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/components/email-me/email-me.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { NgForm, FormBuilder, Validators, FormGroup } from '@angular/forms';
3 |
4 | import { UserService, EmailValidator } from '@shared';
5 |
6 | @Component({
7 | selector: 'app-email-me',
8 | templateUrl: './email-me.component.html',
9 | styleUrls: ['./email-me.component.scss']
10 | })
11 | export class EmailMeComponent {
12 | public form: FormGroup;
13 |
14 | constructor(
15 | private userService: UserService,
16 | public formBuilder: FormBuilder) {
17 | this.form = formBuilder.group({
18 | email: ['', Validators.compose([Validators.required, EmailValidator.isValid])]
19 | });
20 | }
21 |
22 | public onSubmit(form: NgForm) {
23 | const email = form.value.email;
24 | return this.userService.keepInTouch(email);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/components/misc/carousel/carousel.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{image.title}}
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/app/components/misc/carousel/carousel.component.scss:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | width: 100%;
3 | margin: 0px auto 10px;
4 | }
5 |
6 | .carousel {
7 | overflow:hidden;
8 | width:100%;
9 | }
10 |
11 | .slides {
12 | list-style:none;
13 | position:relative;
14 | width:500%; /* Each slide is 100% */
15 | overflow:hidden;
16 | -moz-animation:carousel 30s infinite;
17 | -webkit-animation:carousel 30s infinite;
18 | animation:carousel 30s infinite;
19 | padding: inherit;
20 | }
21 |
22 | .slides > li {
23 | position:relative;
24 | float:left;
25 | width: 20%; /* Total of slides should be 100% */
26 | }
27 |
28 | .carousel img {
29 | display:block;
30 | width:100%;
31 | max-width:100%;
32 | }
33 |
34 | .carousel h2 {
35 | margin-bottom: 0;
36 | font-size:1em;
37 | padding:1.5em 0.5em 1.5em 0.5em;
38 | position:absolute;
39 | right:0px;
40 | bottom:0px;
41 | left:0px;
42 | text-align:center;
43 | color:#fff;
44 | background-color:rgba(0,0,0,0.75);
45 | text-transform: uppercase;
46 | }
47 |
48 | @keyframes carousel {
49 | 0% { left:0%; }
50 | 11% { left:0%; }
51 | 12.5% { left:-100%; }
52 | 23.5% { left:-100%; }
53 | 25% { left:-200%; }
54 | 36% { left:-200%; }
55 | 37.5% { left:-300%; }
56 | 48.5% { left:-300%; }
57 | 50% { left:-400%; }
58 | 61% { left:-400%; }
59 | 62.5% { left:-300%; }
60 | 73.5% { left:-300%; }
61 | 75% { left:-200%; }
62 | 86% { left:-200%; }
63 | 87.5% { left:-100%; }
64 | 98.5% { left:-100%; }
65 | 100% { left:0%; }
66 | }
67 |
--------------------------------------------------------------------------------
/src/app/components/misc/carousel/carousel.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Image } from '@shared';
3 |
4 | @Component({
5 | selector: 'app-carousel',
6 | templateUrl: './carousel.component.html',
7 | styleUrls: ['./carousel.component.scss'],
8 | })
9 |
10 | export class CarouselComponent {
11 | public images = IMAGES;
12 | constructor() {
13 | }
14 | }
15 |
16 | const IMAGES: Image[] = [
17 | { 'title': 'Carousel slide nr 1', 'url': '/assets/img/mb-bg-06.png' },
18 | { 'title': 'Carousel slide nr 2', 'url': '/assets/img/mb-bg-05.png' },
19 | { 'title': 'Carousel slide nr 3', 'url': '/assets/img/mb-bg-06.png' },
20 | { 'title': 'Carousel slide nr 4', 'url': '/assets/img/mb-bg-05.png' },
21 | { 'title': 'Carousel slide nr 5', 'url': '/assets/img/mb-bg-06.png' },
22 | ];
23 |
--------------------------------------------------------------------------------
/src/app/components/misc/change/change-detail.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input,
2 | ChangeDetectionStrategy, DoCheck,
3 | KeyValueDiffers } from '@angular/core';
4 |
5 | @Component({
6 | selector: 'app-change-detail',
7 | template: `
8 |
9 |
10 | Change Detail :
11 |
12 |
DetectChanges
13 |
14 |
{{ course | json }}
15 |
16 | `,
17 | styles: [],
18 | changeDetection: ChangeDetectionStrategy.OnPush
19 | })
20 | export class ChangeDetailComponent implements OnInit, DoCheck {
21 |
22 | @Input() course;
23 | differ;
24 |
25 | constructor(private differs: KeyValueDiffers) {
26 | this.differ = differs.find(Object).create();
27 | }
28 |
29 | OnMouseEnter(e) {
30 | console.log(e);
31 | }
32 |
33 | ngOnInit() {
34 | }
35 |
36 | OnDetectChanges() {
37 | }
38 |
39 | ngDoCheck(): void {
40 | console.log(this.course);
41 |
42 | const changes = this.differ.diff(this.course);
43 |
44 | if (changes) {
45 | console.log('changes detected');
46 | changes.forEachChangedItem(r => console.log('changed ', r.currentValue, r));
47 | changes.forEachAddedItem(r => console.log('added ' + r.currentValue, r));
48 | changes.forEachRemovedItem(r => console.log('removed ' + r.currentValue));
49 | } else {
50 | console.log('nothing changed');
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/app/components/misc/change/change-detection.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, KeyValueDiffers } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-change-detection',
5 | template: `
6 |
7 | Change Detection :
8 |
9 | Mutate
10 | Change
11 |
12 | {{change2 | json}}
13 |
14 | `,
15 | styles: [`.source {
16 | background-color: #ccc
17 | }`]
18 | })
19 | export class ChangeDetectionComponent implements OnInit {
20 |
21 | change1: any;
22 | change2: any;
23 |
24 | ngOnInit() {
25 | this.change1 = {title: 'Learning Angular', author: 'Person'};
26 | this.change2 = this.change1;
27 | }
28 |
29 | OnMutate() {
30 | console.log('OnMutate');
31 | this.change1.title = 'Learning Angular 8';
32 | this.change1.author = 'Person A';
33 | }
34 |
35 | OnChange() {
36 | console.log('OnChange');
37 | this.change1 = {title: 'Learning React', author: 'Person B'};
38 | this.change2 = this.change1;
39 | }
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/components/misc/drag-drop/drag-drop.component.html:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/src/app/components/misc/drag-drop/drag-drop.component.scss:
--------------------------------------------------------------------------------
1 | .drag-drop-container {
2 | width: 400px;
3 | max-width: 100%;
4 | margin: 0 25px 25px 0;
5 | display: inline-block;
6 | vertical-align: top;
7 | }
8 |
9 | .drag-drop-list {
10 | border: solid 1px #ccc;
11 | min-height: 60px;
12 | background: white;
13 | border-radius: 4px;
14 | overflow: hidden;
15 | display: block;
16 | }
17 |
18 | .drag-drop-box {
19 | padding: 20px 10px;
20 | border-bottom: solid 1px #ccc;
21 | color: rgba(0, 0, 0, 0.87);
22 | display: flex;
23 | flex-direction: row;
24 | align-items: center;
25 | justify-content: space-between;
26 | box-sizing: border-box;
27 | cursor: move;
28 | background: white;
29 | font-size: 14px;
30 | }
31 |
32 | .cdk-drag-preview {
33 | box-sizing: border-box;
34 | border-radius: 4px;
35 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
36 | 0 8px 10px 1px rgba(0, 0, 0, 0.14),
37 | 0 3px 14px 2px rgba(0, 0, 0, 0.12);
38 | }
39 |
40 | .cdk-drag-placeholder {
41 | opacity: 0;
42 | }
43 |
44 | .cdk-drag-animating {
45 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
46 | }
47 |
48 | .drag-drop-box:last-child {
49 | border: none;
50 | }
51 |
52 | .drag-drop-list.cdk-drop-list-dragging .drag-drop-box:not(.cdk-drag-placeholder) {
53 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
54 | }
55 |
--------------------------------------------------------------------------------
/src/app/components/misc/drag-drop/drag-drop.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop';
3 |
4 | @Component({
5 | selector: 'app-drag-drop',
6 | templateUrl: 'drag-drop.component.html',
7 | styleUrls: ['drag-drop.component.scss'],
8 | })
9 | export class DragDropComponent {
10 | public todo: string[] = [
11 | 'Add new features',
12 | 'Get 1000 stars'
13 | ];
14 |
15 | public done: string[] = [
16 | 'Angular',
17 | 'Firebase',
18 | 'Material desing',
19 | ];
20 |
21 | public drop(event: CdkDragDrop): void {
22 | if (event.previousContainer === event.container) {
23 | moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
24 | } else {
25 | transferArrayItem(
26 | event.previousContainer.data,
27 | event.container.data,
28 | event.previousIndex,
29 | event.currentIndex
30 | );
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/app/components/misc/expansion-panel/expansion-panel.component.html:
--------------------------------------------------------------------------------
1 | mat expansion panel (accordion) :
2 |
--------------------------------------------------------------------------------
/src/app/components/misc/expansion-panel/expansion-panel.component.scss:
--------------------------------------------------------------------------------
1 | .headers-align .mat-expansion-panel-header-title,
2 | .headers-align .mat-expansion-panel-header-description {
3 | flex-basis: 0;
4 | }
5 |
6 | .headers-align .mat-expansion-panel-header-description {
7 | justify-content: space-between;
8 | align-items: center;
9 | }
--------------------------------------------------------------------------------
/src/app/components/misc/expansion-panel/expansion-panel.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-expansion-panel',
5 | templateUrl: './expansion-panel.component.html',
6 | styleUrls: ['./expansion-panel.component.scss']
7 | })
8 | export class ExpansionPanelComponent implements OnInit {
9 | step = 0;
10 |
11 | constructor() { }
12 |
13 | ngOnInit() {
14 | }
15 |
16 | setStep(index: number) {
17 | this.step = index;
18 | }
19 |
20 | nextStep() {
21 | this.step++;
22 | }
23 |
24 | prevStep() {
25 | this.step--;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/components/misc/misc.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/src/app/components/misc/misc.component.scss:
--------------------------------------------------------------------------------
1 | #content {
2 | padding: 20px;
3 | }
4 |
5 | .container {
6 | height: 300px;
7 | border: 1px solid rgba(0, 0, 0, 0.5);
8 | }
9 |
10 | .sidenav-content {
11 | display: flex;
12 | height: 100%;
13 | align-items: center;
14 | justify-content: center;
15 | }
16 |
17 | .sidenav {
18 | padding: 25px;
19 | }
--------------------------------------------------------------------------------
/src/app/components/misc/misc.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { MatDatepicker } from '@angular/material';
3 |
4 | @Component({
5 | selector: 'app-misc',
6 | templateUrl: './misc.component.html',
7 | styleUrls: ['./misc.component.scss']
8 | })
9 | export class MiscComponent implements OnInit {
10 | answer: string;
11 |
12 | constructor() { }
13 |
14 | ngOnInit() {
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/components/misc/misc.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { MatButtonModule, MatCheckboxModule, MatInputModule, MatNativeDateModule,
5 | MatSlideToggleModule, MatStepperModule, MatTooltipModule, MatSidenavModule,
6 | MatTableModule, MatCardModule, MatDatepickerModule, MatExpansionModule,
7 | MatIconModule, MatToolbarModule } from '@angular/material';
8 |
9 | // import { CdkTableModule } from '@angular/cdk';
10 | import { DragDropModule } from '@angular/cdk/drag-drop';
11 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
12 | import { PipesModule } from '@shared/pipes/pipes.module';
13 |
14 | import { CarouselComponent } from './carousel/carousel.component';
15 | import { MiscComponent } from './misc.component';
16 | import { ChangeDetectionComponent } from './change/change-detection.component';
17 | import { ChangeDetailComponent } from './change/change-detail.component';
18 | import { VirtRealComponent } from './virtual-reality/virtreal.component';
19 | import { TableComponent } from './table/table.component';
20 | import { StepperComponent } from './stepper/stepper.component';
21 | import { DragDropComponent } from './drag-drop/drag-drop.component';
22 | import { ExpansionPanelComponent } from './expansion-panel/expansion-panel.component';
23 |
24 | @NgModule({
25 | declarations: [
26 | CarouselComponent,
27 | MiscComponent,
28 | ChangeDetectionComponent,
29 | ChangeDetailComponent,
30 | VirtRealComponent,
31 | TableComponent,
32 | StepperComponent,
33 | DragDropComponent,
34 | ExpansionPanelComponent
35 | ],
36 | imports: [
37 | CommonModule,
38 | MatButtonModule, MatCheckboxModule, MatInputModule, MatNativeDateModule,
39 | MatSlideToggleModule, MatStepperModule, MatTooltipModule, MatSidenavModule,
40 | MatTableModule, MatCardModule, MatDatepickerModule, MatExpansionModule,
41 | MatIconModule, MatToolbarModule,
42 | FormsModule, ReactiveFormsModule,
43 | PipesModule,
44 | DragDropModule
45 | ],
46 | exports: [
47 | CarouselComponent,
48 | MiscComponent,
49 | ChangeDetectionComponent,
50 | ChangeDetailComponent,
51 | VirtRealComponent,
52 | TableComponent,
53 | StepperComponent,
54 | ExpansionPanelComponent
55 | ],
56 | schemas: [
57 | CUSTOM_ELEMENTS_SCHEMA
58 | ]
59 | })
60 |
61 | export class MiscModule {
62 | }
63 |
--------------------------------------------------------------------------------
/src/app/components/misc/stepper/stepper.component.html:
--------------------------------------------------------------------------------
1 | mat stepper :
2 | Enable linear mode
3 |
4 |
5 |
6 |
15 |
16 |
17 |
27 |
28 |
29 | Done
30 | You are now done.
31 |
32 | Back
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/app/components/misc/stepper/stepper.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/app/components/misc/stepper/stepper.component.scss
--------------------------------------------------------------------------------
/src/app/components/misc/stepper/stepper.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { FormBuilder, FormGroup, Validators } from '@angular/forms';
3 |
4 | import { EmailValidator } from '@shared';
5 |
6 | @Component({
7 | selector: 'app-stepper',
8 | templateUrl: './stepper.component.html',
9 | styleUrls: ['./stepper.component.scss']
10 | })
11 | export class StepperComponent implements OnInit {
12 | isLinear = false;
13 | firstFormGroup: FormGroup;
14 | secondFormGroup: FormGroup;
15 |
16 | constructor(private _formBuilder: FormBuilder) { }
17 |
18 | ngOnInit() {
19 | this.firstFormGroup = this._formBuilder.group({
20 | name: ['', Validators.required]
21 | });
22 | this.secondFormGroup = this._formBuilder.group({
23 | email: ['', Validators.compose([Validators.required, EmailValidator.isValid])]
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/components/misc/table/table.component.html:
--------------------------------------------------------------------------------
1 | mat/CDK Table :
2 |
3 |
4 |
5 |
6 | ID
7 | {{row.id}}
8 |
9 |
10 |
11 | Progress
12 | {{row.progress}}%
13 |
14 |
15 |
16 | Name
17 | {{row.name}}
18 |
19 |
20 |
21 | Color
22 | {{row.color}}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/app/components/misc/table/table.component.scss:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | flex-direction: column;
4 | height: 300px;
5 | min-width: 300px;
6 | }
7 |
8 | .header {
9 | min-height: 64px;
10 | display: flex;
11 | align-items: center;
12 | padding-left: 24px;
13 | font-size: 20px;
14 | }
15 |
16 | .mat-table {
17 | overflow: auto;
18 | }
19 |
20 | .mat-header-cell .mat-sort-header-sorted {
21 | color: black;
22 | }
--------------------------------------------------------------------------------
/src/app/components/misc/table/table.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ViewChild } from '@angular/core';
2 | import { DataSource } from '@angular/cdk/collections';
3 | import { MatSort } from '@angular/material';
4 | import { BehaviorSubject } from 'rxjs/BehaviorSubject';
5 | import { Observable } from 'rxjs/Observable';
6 |
7 | import 'rxjs/add/operator/startWith';
8 | import 'rxjs/add/observable/merge';
9 | import 'rxjs/add/operator/map';
10 |
11 | @Component({
12 | selector: 'app-table',
13 | templateUrl: './table.component.html',
14 | styleUrls: ['./table.component.scss']
15 | })
16 | export class TableComponent implements OnInit {
17 | displayedColumns = ['userId', 'userName', 'progress', 'color'];
18 | database = new DataBase();
19 | dataSource: dataSource | null;
20 |
21 | @ViewChild(MatSort, {static: true}) sort: MatSort;
22 |
23 | constructor() { }
24 |
25 | ngOnInit() {
26 | this.dataSource = new dataSource(this.database, this.sort);
27 | }
28 | }
29 |
30 | // Local data
31 | const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
32 | 'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
33 | const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
34 | 'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
35 | 'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];
36 |
37 | export interface UserData {
38 | id: string;
39 | name: string;
40 | progress: string;
41 | color: string;
42 | }
43 |
44 | export class DataBase {
45 | // Stream that emits whenever the data has been modified
46 | dataChange: BehaviorSubject = new BehaviorSubject([]);
47 | get data(): UserData[] { return this.dataChange.value; }
48 |
49 | constructor() {
50 | // Fill up the database with 100 users
51 | for (let i = 0; i < 100; i++) { this.addUser(); }
52 | }
53 |
54 | // Adds a new user to the database
55 | addUser() {
56 | const copiedData = this.data.slice();
57 | copiedData.push(this.createNewUser());
58 | this.dataChange.next(copiedData);
59 | }
60 |
61 | // Builds and returns a new User
62 | private createNewUser() {
63 | const name =
64 | NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
65 | NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
66 |
67 | return {
68 | id: (this.data.length + 1).toString(),
69 | name: name,
70 | progress: Math.round(Math.random() * 100).toString(),
71 | color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
72 | };
73 | }
74 | }
75 |
76 | // Data source to provide what data should be rendered in the table
77 | export class dataSource extends DataSource {
78 | constructor(private database: DataBase, private sort: MatSort) {
79 | super();
80 | }
81 |
82 | connect(): Observable {
83 | const displayDataChanges = [
84 | this.database.dataChange,
85 | this.sort.sortChange
86 | ];
87 |
88 | return Observable.merge(...displayDataChanges).map(() => {
89 | return this.getSortedData();
90 | });
91 | }
92 |
93 | disconnect() {}
94 |
95 | // Sorting the database data
96 | getSortedData(): UserData[] {
97 | const data = this.database.data.slice();
98 | if (!this.sort.active || this.sort.direction === '') { return data; }
99 |
100 | return data.sort((a, b) => {
101 | let propertyA: number|string = '';
102 | let propertyB: number|string = '';
103 |
104 | switch (this.sort.active) {
105 | case 'userId': [propertyA, propertyB] = [a.id, b.id]; break;
106 | case 'userName': [propertyA, propertyB] = [a.name, b.name]; break;
107 | case 'progress': [propertyA, propertyB] = [a.progress, b.progress]; break;
108 | case 'color': [propertyA, propertyB] = [a.color, b.color]; break;
109 | }
110 |
111 | const valueA = isNaN(+propertyA) ? propertyA : +propertyA;
112 | const valueB = isNaN(+propertyB) ? propertyB : +propertyB;
113 |
114 | return (valueA < valueB ? -1 : 1) * (this.sort.direction === 'asc' ? 1 : -1);
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/app/components/misc/virtual-reality/virtreal.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | 360 view (Works only at localhost) :
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/app/components/misc/virtual-reality/virtreal.component.scss:
--------------------------------------------------------------------------------
1 | #content {
2 | padding: 20px;
3 | }
--------------------------------------------------------------------------------
/src/app/components/misc/virtual-reality/virtreal.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, AfterViewInit, Input, ViewChild } from '@angular/core';
2 | import { Scene } from '@shared'
3 |
4 | declare let VRView: any;
5 |
6 | @Component({
7 | selector: 'app-virtreal',
8 | templateUrl: './virtreal.component.html',
9 | styleUrls: ['./virtreal.component.scss']
10 | })
11 | export class VirtRealComponent implements AfterViewInit {
12 |
13 | viewer: any;
14 | @ViewChild('viewer', {static: true}) viewerElement: any;
15 |
16 | @Input() scenes: Scene;
17 | @Input() width: any;
18 | @Input() height: any;
19 |
20 | constructor() {
21 | }
22 |
23 | ngAfterViewInit() {
24 | this.viewer = new VRView.Player(`#${this.viewerElement.nativeElement.id}`, {
25 | image: 'assets/img/mountsthelens.png',
26 | width: 1200,
27 | height: 250
28 | });
29 |
30 | this.viewer.on('ready', () => {
31 |
32 | this.loadScene('world');
33 | this.viewer.on('click', (event) => event.id ? this.loadScene(event.id) : '');
34 | });
35 | }
36 |
37 | loadScene(id) {
38 | if (id) {
39 | // Set the image
40 | this.viewer.setContent({
41 | image: this.scenes[id].image,
42 | // preview: this.scenes[id].preview,
43 | });
44 | // Add all the hotspots for the scene
45 | const newScene = this.scenes[id];
46 | const sceneHotspots = Object.keys(newScene.hotspots);
47 | for (let i = 0; i < sceneHotspots.length; i++) {
48 | const hotspotKey = sceneHotspots[i];
49 | const hotspot = newScene.hotspots[hotspotKey];
50 |
51 | this.viewer.addHotspot(hotspotKey, {
52 | pitch: hotspot.pitch,
53 | yaw: hotspot.yaw,
54 | radius: hotspot.radius,
55 | distance: hotspot.distance
56 | });
57 | }
58 | }
59 | }
60 |
61 | getFirstScene() {
62 | for (const key in this.scenes) {
63 | if (this.scenes.hasOwnProperty(key)) {
64 | return key;
65 | }
66 | }
67 | return 0;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/app/firebase.config.ts:
--------------------------------------------------------------------------------
1 | export const firebaseKeys = {
2 | // For your own projects please use different keys
3 | apiKey: 'AIzaSyBUYZcc_HKi1TckbZPpSjSkxyFvml3Is0A',
4 | authDomain: 'angular4materialdesign.firebaseapp.com',
5 | databaseURL: 'https://angular4materialdesign.firebaseio.com',
6 | projectId: 'angular4materialdesign',
7 | storageBucket: 'angular4materialdesign.appspot.com',
8 | messagingSenderId: '136985605193'
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/pages/about-me/about-me.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | About me
4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eleifend metus vel imperdiet cursus.
5 | Donec tempor placerat nibh ac hendrerit. Nulla feugiat augue ac fringilla facilisis. Aenean eget nibh at quam egestas commodo.
6 | Maecenas dapibus nisl quis nulla tristique, vitae laoreet tellus convallis. Interdum et malesuada fames ac ante ipsum primis in faucibus.
7 | In ultricies mattis rhoncus. Duis vestibulum tristique massa a fringilla. Suspendisse ut purus at massa elementum sollicitudin eu sit amet lacus.
8 | Nullam sit amet nisi risus. In diam dui, aliquet a lectus eu, suscipit finibus lorem. Interdum et malesuada fames ac ante ipsum primis in faucibus.
9 | Integer vitae facilisis felis, quis cursus velit. Suspendisse nec consectetur mauris, ut placerat ex. Duis eu consectetur eros. Nulla facilisi.
10 |
11 |
12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eleifend metus vel imperdiet cursus.
13 | Etiam pretium sem a metus tristique, ut luctus quam rutrum. Vivamus commodo venenatis nunc ut placerat.
14 | Proin eleifend id dui sit amet imperdiet. Proin fringilla sit amet nunc sed consequat. Proin ac libero elit.
15 | Pellentesque magna purus, semper gravida porttitor nec, venenatis vitae urna. Nam luctus id eros eget convallis.
16 | Aliquam sem libero, mollis et leo id, semper finibus leo. Sed convallis leo ac tempor luctus. Integer metus arcu,
17 | imperdiet nec tincidunt at, sodales nec nulla. Nam suscipit urna eu vulputate aliquet. Vestibulum consequat nisl vel erat congue sollicitudin.
18 | Proin mattis ultrices blandit. Vivamus accumsan vulputate sem vel tincidunt. Etiam imperdiet imperdiet ante, sit amet suscipit tortor finibus maximus.
19 | Maecenas euismod pulvinar lorem.
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/app/pages/about-me/about-me.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { AboutMeComponent } from './about-me.component';
7 |
8 | describe('AboutMeComponent', () => {
9 | let component: AboutMeComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ AboutMeComponent ]
15 | })
16 | .compileComponents();
17 | }));
18 |
19 | beforeEach(() => {
20 | fixture = TestBed.createComponent(AboutMeComponent);
21 | component = fixture.componentInstance;
22 | fixture.detectChanges();
23 | });
24 |
25 | it('should create', () => {
26 | expect(component).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/pages/about-me/about-me.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-about-me',
5 | templateUrl: './about-me.component.html',
6 | styles: [`
7 | #content {
8 | padding: 20px;
9 | }
10 | `]
11 | })
12 | export class AboutMeComponent {}
13 |
--------------------------------------------------------------------------------
/src/app/pages/auth/auth.component.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/src/app/pages/auth/auth.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/app/pages/auth/auth.component.scss
--------------------------------------------------------------------------------
/src/app/pages/auth/auth.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { NgForm } from '@angular/forms';
3 |
4 | import { AuthService } from '@shared';
5 |
6 | @Component({
7 | selector: 'app-auth',
8 | templateUrl: './auth.component.html',
9 | styleUrls: ['./auth.component.scss']
10 | })
11 | export class AuthComponent {
12 |
13 | constructor(private authService: AuthService) {}
14 |
15 | public onSuccess(): void {
16 | return this.authService.onSuccess();
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/pages/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | // Modules 3rd party
2 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
3 | import { CommonModule } from '@angular/common';
4 | import { BrowserModule } from '@angular/platform-browser';
5 | import { FormsModule } from '@angular/forms';
6 | import { MatButtonModule, MatInputModule, MatProgressBarModule,
7 | MatCardModule, MatIconModule } from '@angular/material';
8 | import { NgxAuthFirebaseUIModule } from 'ngx-auth-firebaseui';
9 |
10 | // Components
11 | import { AuthComponent } from './auth.component';
12 | import { PhoneSigninComponent } from './phone-signin/phone-signin.component';
13 |
14 | @NgModule({
15 | declarations: [
16 | AuthComponent,
17 | PhoneSigninComponent
18 | ],
19 | imports: [
20 | CommonModule,
21 | BrowserModule,
22 | FormsModule,
23 | MatButtonModule, MatInputModule, MatProgressBarModule,
24 | MatCardModule, MatIconModule,
25 | NgxAuthFirebaseUIModule
26 | ],
27 | providers: [
28 | ],
29 | schemas: [
30 | CUSTOM_ELEMENTS_SCHEMA
31 | ],
32 | exports: [
33 | AuthComponent,
34 | PhoneSigninComponent
35 | ]
36 | })
37 | export class AuthModule {
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/pages/auth/phone-signin/phone-signin.component.html:
--------------------------------------------------------------------------------
1 |
2 |
34 |
35 |
36 |
37 | You have successfully logged in with your phone number!
38 |
39 | UserId: {{ currentUser?.uid }}
40 |
41 |
--------------------------------------------------------------------------------
/src/app/pages/auth/phone-signin/phone-signin.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/app/pages/auth/phone-signin/phone-signin.component.scss
--------------------------------------------------------------------------------
/src/app/pages/auth/phone-signin/phone-signin.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { PhoneSigninComponent } from './phone-signin.component';
4 |
5 | describe('PhoneSigninComponent', () => {
6 | let component: PhoneSigninComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ PhoneSigninComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(PhoneSigninComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should be created', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/pages/auth/phone-signin/phone-signin.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import * as firebase from 'firebase';
4 |
5 | import { WindowService, PhoneNumber, AuthService, AlertService } from '@shared';
6 |
7 | @Component({
8 | selector: 'app-phone-signin',
9 | templateUrl: './phone-signin.component.html',
10 | styleUrls: ['./phone-signin.component.scss']
11 | })
12 | export class PhoneSigninComponent implements OnInit {
13 | phoneNumber = new PhoneNumber()
14 | isAuthenticated: string;
15 |
16 | token: string;
17 | windowRef: any;
18 | verificationCode: string;
19 | currentUser: any;
20 |
21 | constructor(private win: WindowService,
22 | private router: Router,
23 | private authService: AuthService,
24 | private alertService: AlertService) {
25 | this.isAuthenticated = this.authService.isAuthenticated()
26 | }
27 |
28 | ngOnInit() {
29 | this.windowRef = this.win.windowRef
30 | this.windowRef.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container')
31 |
32 | this.windowRef.recaptchaVerifier.render()
33 | }
34 |
35 | sendLoginCode() {
36 | const appVerifier = this.windowRef.recaptchaVerifier;
37 | const num = this.phoneNumber.e164;
38 | firebase.auth().signInWithPhoneNumber(num, appVerifier)
39 | .then(result => {
40 | this.windowRef.confirmationResult = result;
41 | this.alertService.showToaster('Login code is send');
42 | })
43 | .catch( error => console.log(error) );
44 | }
45 |
46 | verifyLoginCode() {
47 | this.windowRef.confirmationResult
48 | .confirm(this.verificationCode)
49 | .then((result) => {
50 | const currentUser = result.user;
51 | })
52 | .then(response => {
53 | this.router.navigate(['/']);
54 | firebase.auth().currentUser.getIdToken()
55 | .then(
56 | (token: string) => this.token = token
57 | );
58 | this.alertService.showToaster('Login code is entered');
59 | })
60 | .catch( error => console.log(error, 'Incorrect code entered?'));
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/app/pages/contact/contact.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Contact us
4 |
42 |
43 |
--------------------------------------------------------------------------------
/src/app/pages/contact/contact.component.scss:
--------------------------------------------------------------------------------
1 | .form {
2 | width: 500px;
3 | }
4 |
5 | .full-width {
6 | width: 100%;
7 | }
8 |
9 | #content {
10 | padding: 20px;
11 | }
--------------------------------------------------------------------------------
/src/app/pages/contact/contact.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { ContactComponent } from './contact.component';
7 |
8 | describe('ContactComponent', () => {
9 | let component: ContactComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ ContactComponent ]
15 | })
16 | .compileComponents();
17 | }));
18 |
19 | beforeEach(() => {
20 | fixture = TestBed.createComponent(ContactComponent);
21 | component = fixture.componentInstance;
22 | fixture.detectChanges();
23 | });
24 |
25 | it('should create', () => {
26 | expect(component).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/pages/contact/contact.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { NgForm } from '@angular/forms';
3 |
4 | import { UserService } from '@shared';
5 |
6 | @Component({
7 | selector: 'app-contact',
8 | templateUrl: './contact.component.html',
9 | styleUrls: ['./contact.component.scss']
10 | })
11 | export class ContactComponent {
12 |
13 | constructor(private userService: UserService) {}
14 |
15 | public onSubmit(form: NgForm) {
16 | const company = form.value.company;
17 | const firstname = form.value.firstname;
18 | const lastname = form.value.lastname;
19 | const address = form.value.address;
20 | const city = form.value.city;
21 | const postal = form.value.postal;
22 | const message = form.value.message;
23 | return this.userService.contactFormSend(company, firstname, lastname, address, city, postal, message);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/pages/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Top
18 |
--------------------------------------------------------------------------------
/src/app/pages/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-home',
5 | templateUrl: 'home.component.html',
6 | styles: [`
7 | button {
8 | position: fixed;
9 | bottom: 70px;
10 | float: right;
11 | right: 10px;
12 | z-index: 10;
13 | }`]
14 | })
15 |
16 | export class HomeComponent {
17 |
18 | public onToTop(): void {
19 | document.body.scrollTop = document.documentElement.scrollTop = 0;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/pages/not-found/not-found.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | template: `
5 |
6 |
7 | Page not found 404
8 | The page you're looking for doesn't exist, please return to the homepage .
9 |
10 |
11 | `,
12 | styles: ['#content { padding: 20px;}']
13 | })
14 |
15 | export class PageNotFoundComponent {}
16 |
--------------------------------------------------------------------------------
/src/app/pages/profile/profile-settings.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | My profile settings
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Save Settings
19 |
20 |
21 |
22 | Logout
23 |
24 |
25 |
26 | Reset Password
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/app/pages/profile/profile-settings.component.scss:
--------------------------------------------------------------------------------
1 | .profile-settings {
2 | padding: 20px;
3 | }
4 |
5 | form {
6 | text-align: center;
7 | }
8 |
9 | mat-card {
10 | width: 400px;
11 | height: 530px;
12 | margin: 100px auto;
13 | }
14 |
15 | mat-form-field {
16 | width: 100%;
17 | }
18 |
19 | textarea {
20 | resize: none;
21 | }
22 |
23 | .avatar {
24 | margin: 10px auto;
25 | width: 120px;
26 | }
27 |
28 | a:hover {
29 | text-decoration: none;
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/pages/profile/profile-settings.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { NgForm } from '@angular/forms';
3 |
4 | import * as firebase from 'firebase';
5 |
6 | import { AuthService, AlertService, UserService } from '@shared';
7 |
8 | @Component({
9 | selector: 'app-profile-settings',
10 | templateUrl: './profile-settings.component.html',
11 | styleUrls: ['./profile-settings.component.scss']
12 | })
13 | export class ProfileSettingsComponent implements OnInit {
14 | public uid = firebase.auth().currentUser.uid;
15 | public displayName: string = 'Your username';
16 | public bio: any = 'Your bio';
17 |
18 | constructor(
19 | private authService: AuthService,
20 | private alertService: AlertService,
21 | private userService: UserService) {
22 | }
23 |
24 | public ngOnInit(): Promise {
25 | return firebase.database().ref().child(`users/${this.uid}`).once('value').then((snap) => {
26 | this.displayName = snap.val().displayName;
27 | this.bio = snap.val().bio;
28 | });
29 | }
30 |
31 | public onPasswordReset(): void {
32 | this.userService.sendUserPasswordResetEmail();
33 | this.alertService.showToaster('Reset password is sent to your email');
34 | }
35 |
36 | public onUpdateUserInfo(form: NgForm): void {
37 | const displayName = form.value.displayName;
38 | const bio = form.value.bio;
39 | this.userService.updateUserInfo(firebase.auth().currentUser.uid, displayName, bio);
40 | this.alertService.showToaster('Your settings are saved');
41 | }
42 |
43 | public onLogout(): void {
44 | this.authService.logout();
45 | this.alertService.showToaster('Logout succesful');
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/pages/profile/profile.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{profileTitle}}
7 | {{userEmail()}}
8 | {{displayName}}
9 |
10 |
11 |
12 |
13 |
14 | {{bio}}
15 |
16 |
17 | Reset my password
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/app/pages/profile/profile.component.scss:
--------------------------------------------------------------------------------
1 | .profile {
2 | padding: 20px;
3 | }
4 |
5 | mat-card {
6 | width: 400px;
7 | height: 460px;
8 | margin: 100px auto;
9 | }
10 |
11 | mat-card-header{
12 | display: inline;
13 | }
14 |
15 | mat-card-header-text {
16 | margin: 0 auto;
17 | }
18 |
19 | mat-card-title {
20 | text-align: center;
21 | }
22 |
23 | .avatar {
24 | margin: 10px auto;
25 | width: 120px;
26 | }
27 |
28 | .avatar img {
29 | width: 120px;
30 | height: 120px;
31 | background-size: cover;
32 | background-position: center;
33 | border-radius: 50%;
34 | }
35 |
36 | a:hover {
37 | text-decoration: none;
38 | }
39 |
40 | .button {
41 | position: absolute;
42 | bottom: 35px;
43 | margin: 0 auto;
44 | }
--------------------------------------------------------------------------------
/src/app/pages/profile/profile.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';
3 |
4 | import * as firebase from 'firebase';
5 |
6 | import { UserService, AlertService } from '@shared';
7 |
8 | @Component({
9 | selector: 'app-profile',
10 | templateUrl: './profile.component.html',
11 | styleUrls: ['./profile.component.scss'],
12 | animations: [
13 | trigger('imageAnimation', [
14 |
15 | state('small', style({
16 | transform: 'scale(1)',
17 | })),
18 | state('large', style({
19 | transform: 'scale(2)',
20 | })),
21 |
22 | transition('small <=> large', animate('500ms ease-in', keyframes([
23 | style({opacity: 0, transform: 'translateY(-80%)', offset: 0}),
24 | style({opacity: 1, transform: 'translateY(25px)', offset: 1})
25 | ]))),
26 | ]),
27 | ]
28 | })
29 | export class ProfileComponent implements OnInit {
30 | public uid = firebase.auth().currentUser.uid;
31 |
32 | public fullImagePath: string = '/assets/img/mb-bg-04.png';
33 | public profileTitle: string = 'My profile';
34 | public displayName: string = 'Your username';
35 | public bio: any = 'Your bio';
36 | public state: string = 'small';
37 |
38 | constructor(
39 | private userService: UserService,
40 | private alertService: AlertService) {}
41 |
42 | public ngOnInit(): Promise {
43 | return firebase.database().ref().child('users/' + this.uid).once('value').then((snap) => {
44 | this.displayName = snap.val().displayName,
45 | this.bio = snap.val().bio
46 | });
47 | }
48 |
49 | public animateImage(): void {
50 | this.state = (this.state === 'small' ? 'large' : 'small');
51 | }
52 |
53 | public userEmail(): void {
54 | this.userService.getUserProfileInformation();
55 | firebase.auth().currentUser.email;
56 | }
57 |
58 | public onPasswordReset(): void {
59 | this.userService.sendUserPasswordResetEmail();
60 | this.alertService.showToaster('Reset password is sent to your email');
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/app/pages/profile/profile.module.ts:
--------------------------------------------------------------------------------
1 | // Modules 3rd party
2 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
3 | import { CommonModule } from '@angular/common';
4 | import { MatButtonModule, MatCheckboxModule, MatMenuModule, MatInputModule,
5 | MatToolbarModule, MatCardModule, MatDialogModule } from '@angular/material';
6 | import { FormsModule } from '@angular/forms';
7 |
8 | // Components
9 | import { ProfileComponent } from './profile.component';
10 | import { ProfileSettingsComponent } from './profile-settings.component';
11 |
12 | @NgModule({
13 | declarations: [
14 | ProfileComponent,
15 | ProfileSettingsComponent
16 | ],
17 | imports: [
18 | CommonModule,
19 | MatButtonModule, MatCheckboxModule, MatMenuModule, MatInputModule,
20 | MatToolbarModule, MatCardModule, MatDialogModule,
21 | FormsModule
22 | ],
23 | exports: [
24 | ProfileComponent,
25 | ProfileSettingsComponent
26 | ],
27 | schemas: [
28 | CUSTOM_ELEMENTS_SCHEMA
29 | ]
30 | })
31 | export class ProfileModule {
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/shared/index.ts:
--------------------------------------------------------------------------------
1 | export * from './models';
2 | export * from './layout';
3 | export * from './services';
4 | export * from './interfaces';
5 | export * from './validators';
6 |
--------------------------------------------------------------------------------
/src/app/shared/interfaces/image.interface.ts:
--------------------------------------------------------------------------------
1 | export interface Image {
2 | title: string;
3 | url: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/shared/interfaces/index.ts:
--------------------------------------------------------------------------------
1 | export * from './scene.interface';
2 | export * from './image.interface';
3 |
--------------------------------------------------------------------------------
/src/app/shared/interfaces/scene.interface.ts:
--------------------------------------------------------------------------------
1 | export interface Scene {
2 | [key: string]: {
3 | image: string;
4 | hotspots?: Hotspot
5 | };
6 | }
7 |
8 | export interface Hotspot {
9 | [key: string]: {
10 | pitch: number;
11 | yaw: number;
12 | radius: number,
13 | distance: number
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/shared/layout/footer/footer.component.html:
--------------------------------------------------------------------------------
1 |
2 | ©
3 |
4 | mail_outline
5 |
--------------------------------------------------------------------------------
/src/app/shared/layout/footer/footer.component.scss:
--------------------------------------------------------------------------------
1 | .icon {
2 | padding: 0 14px;
3 | }
4 |
5 | mat-icon {
6 | color:rgba(255,255,255,.87);
7 | }
8 |
9 | .spacer {
10 | flex: 1 1 auto;
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/shared/layout/footer/footer.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-footer',
5 | templateUrl: 'footer.component.html',
6 | styleUrls: ['./footer.component.scss']
7 | })
8 | export class FooterComponent {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/shared/layout/header/header.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | menu
55 |
56 |
57 |
58 |
59 |
60 |
61 |
64 |
65 |
66 |
67 |
68 |
69 | lock_open
70 |
71 |
72 |
73 |
74 |
75 | Welcome
76 |
77 |
78 | more_vert
79 |
80 |
81 |
82 |
85 | face
86 | Profile
87 |
88 |
89 |
90 | {{item.icon}}
91 | {{item.title}}
92 |
93 |
94 |
99 | settings
100 | Settings
101 |
102 |
103 |
104 | lock_outline
105 | Logout
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/src/app/shared/layout/header/header.component.scss:
--------------------------------------------------------------------------------
1 | // Header
2 | #content {
3 | padding: 20px;
4 | }
5 |
6 | .spacer {
7 | flex: 1 1 auto;
8 | }
9 |
10 | .spacer-light {
11 | margin: 0 10px;
12 | }
13 |
14 | .mat-toolbar.mat-primary {
15 | box-shadow: 0 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12);
16 | }
17 |
18 | span a, span a:hover {
19 | text-decoration: none;
20 | color:rgba(255,255,255,.87);
21 | }
22 |
23 | mat-icon {
24 | margin-right: 0px!important;
25 | }
26 |
27 | .menu-link a, .menu-link a:hover {
28 | font-size: 14px;
29 | text-decoration: none;
30 | color:rgba(255,255,255,.87);
31 | margin: 0 10px;
32 | }
33 |
34 | a, a:hover {
35 | text-decoration: none;
36 | color:rgba(0,0,0,.87);
37 | }
38 |
39 | .mat-icon {
40 | text-decoration: none;
41 | color:rgba(255,255,255,.87);
42 | }
43 |
44 | .login, .logout, .profile, .messages, .settings, .home, .about, .contact, .misc, .signup{
45 | color: #333;
46 | }
47 |
48 | // Sidenav
49 | .sidenav-content {
50 | display: flex;
51 | height: 100%;
52 | align-items: center;
53 | justify-content: center;
54 | }
55 |
56 | .sidenav {
57 | width: 180px;
58 | z-index: 100;
59 | }
60 |
61 | .menu-link button, .menu-link a:hover {
62 | width: 100%;
63 | height: 48px;
64 | font-size: 14px;
65 | line-height: 48px;
66 | padding: 0 16px;
67 | text-decoration: none;
68 | text-align: left;
69 | }
70 |
71 | span {
72 | margin-left: 15px;
73 | }
74 |
75 | img {
76 | margin: 15px 15px 0;
77 | max-width: 150px;
78 | max-height: 180px;
79 | }
80 |
--------------------------------------------------------------------------------
/src/app/shared/layout/header/header.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import * as firebase from 'firebase';
3 |
4 | import { AuthService, AlertService } from '../../services';
5 |
6 | @Component({
7 | selector: 'app-header',
8 | templateUrl: 'header.component.html',
9 | styleUrls: ['./header.component.scss'],
10 | })
11 | export class HeaderComponent {
12 | public isAuthenticated: string;
13 | public angularImage: string = '/assets/img/angular2.png';
14 |
15 | public menuItems: Object[] = [
16 | {
17 | icon: 'library_books',
18 | title: 'Medium @jeroenouw',
19 | link: 'https://medium.com/@jeroenouw'
20 | },
21 | {
22 | icon: 'description',
23 | title: 'Generated docs',
24 | link: 'http://ngxmatfire-docs.jerouw.nl/'
25 | },
26 | {
27 | icon: 'description',
28 | title: 'Features',
29 | link: 'https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/docs/FEATURES.md'
30 | },
31 | {
32 | icon: 'flight_takeoff',
33 | title: 'Quick start',
34 | link: 'https://github.com/jeroenouw/AngularMaterialFirebase/blob/master/docs/DEVELOPER.md'
35 | },
36 | {
37 | icon: 'archive',
38 | title: 'NPM packages',
39 | link: 'https://www.npmjs.com/~jeroenouw'
40 | },
41 | {
42 | icon: 'link',
43 | title: 'Fork on Github',
44 | link: 'https://github.com/jeroenouw/AngularMaterialFirebase'
45 | },
46 | ];
47 |
48 | constructor(
49 | public authService: AuthService,
50 | private alertService: AlertService,
51 | ) {
52 | this.isAuthenticated = this.authService.isAuthenticated()
53 | }
54 |
55 | public userUid(): string {
56 | return firebase.auth().currentUser.uid;
57 | }
58 |
59 | public userEmail(): string {
60 | return firebase.auth().currentUser.email;
61 | }
62 |
63 | public userName(): string {
64 | return firebase.auth().currentUser.displayName;
65 | }
66 |
67 | public onLogout(): void {
68 | this.alertService.showToaster('Logout succesful');
69 | return this.authService.logout();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/app/shared/layout/index.ts:
--------------------------------------------------------------------------------
1 | export * from './footer/footer.component';
2 | export * from './header/header.component';
3 |
--------------------------------------------------------------------------------
/src/app/shared/models/contact.model.ts:
--------------------------------------------------------------------------------
1 | export class Contact {
2 | constructor(
3 | public company: string,
4 | public firstname: string,
5 | public lastname: string,
6 | public address: string,
7 | public city: string,
8 | public province: string,
9 | public zipcode: string
10 | ) { }
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/shared/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from './profile.model';
2 | export * from './user.model';
3 | export * from './contact.model';
4 | export * from './phone-number.model';
5 |
--------------------------------------------------------------------------------
/src/app/shared/models/phone-number.model.ts:
--------------------------------------------------------------------------------
1 | export class PhoneNumber {
2 | country: string;
3 | area: string;
4 | prefix: string;
5 | line: string;
6 |
7 | get e164() {
8 | const num = this.country + this.area + this.prefix + this.line;
9 | return `+${num}`;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/shared/models/profile.model.ts:
--------------------------------------------------------------------------------
1 | export class Profile {
2 | displayName: string;
3 | email: string;
4 | bio: string;
5 | photoUrl: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/shared/models/user.model.ts:
--------------------------------------------------------------------------------
1 | export class User {
2 | public displayName: string;
3 | public email: string;
4 | public bio: any;
5 | public image: any;
6 | public uid: any;
7 | }
8 |
--------------------------------------------------------------------------------
/src/app/shared/pipes/async-observable-pipe.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Observable } from 'rxjs/Rx';
3 |
4 | @Component({
5 | selector: 'app-async-observable-pipe',
6 | template: `
7 |
8 | Async Observable :
9 |
10 | Count: {{counter$ | async}}
11 |
12 |
13 | `,
14 | styles: []
15 | })
16 | export class AsyncObservablePipeComponent {
17 |
18 | public counter$: Observable;
19 |
20 | constructor() {
21 | this.counter$ = Observable.interval(1000);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/shared/pipes/currency-pipe.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-currency-pipe',
5 | template: `
6 | Currency Pipe :
7 |
8 |
Angular: {{angularPrice | currency:'EUR':false}}
9 |
Angular 8: {{angular7Price | currency:'EUR':true:'4.2-2'}}
10 |
11 | `,
12 | styles: []
13 | })
14 | export class CurrencyPipeComponent {
15 | public angularPrice = 0.567;
16 | public angular7Price = 1.3743;
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/shared/pipes/date-pipe.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-date-pipe',
5 | template: `
6 | {{today | date:'d'}}-{{today | date:'M'}}-{{today | date:'y'}}
7 |
8 | `,
9 | })
10 | export class DatePipeComponent {
11 | public today: number = Date.now();
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/shared/pipes/json-pipe.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-json-pipe',
5 | template: `
6 |
7 |
8 | Json Pipe :
9 | Without JSON pipe:
10 | {{object}}
11 | With JSON pipe:
12 | {{object | json}}
13 |
14 |
15 | `
16 | })
17 | export class JsonPipeComponent {
18 | public object: Object = {abc: 'test', def: 'test2', nested: {def: 3, numbers: [1, 2, 3, 4, 5]}};
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/shared/pipes/pipes.module.ts:
--------------------------------------------------------------------------------
1 | // Modules 3rd party
2 | import { NgModule } from '@angular/core';
3 | import { CommonModule } from '@angular/common';
4 |
5 | // Pipes
6 | import { YearPipeComponent } from './year-pipe.component';
7 | import { DatePipeComponent } from './date-pipe.component';
8 | import { TitleCasePipeComponent } from './titlecase-pipe.component';
9 | import { AsyncObservablePipeComponent } from './async-observable-pipe.component';
10 | import { CurrencyPipeComponent } from './currency-pipe.component';
11 | import { JsonPipeComponent } from './json-pipe.component';
12 |
13 | @NgModule({
14 | declarations: [
15 | YearPipeComponent,
16 | DatePipeComponent,
17 | TitleCasePipeComponent,
18 | AsyncObservablePipeComponent,
19 | CurrencyPipeComponent,
20 | JsonPipeComponent
21 | ],
22 | imports: [
23 | CommonModule
24 | ],
25 | exports: [
26 | YearPipeComponent,
27 | DatePipeComponent,
28 | TitleCasePipeComponent,
29 | AsyncObservablePipeComponent,
30 | CurrencyPipeComponent,
31 | JsonPipeComponent
32 | ]
33 | })
34 | export class PipesModule {
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/shared/pipes/titlecase-pipe.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-titlecase-pipe',
5 | template: `
6 | {{message | titlecase}}
7 | `
8 | })
9 | export class TitleCasePipeComponent {
10 | public message = 'angular material firebase';
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/shared/pipes/year-pipe.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-year-pipe',
5 | template: `
6 | {{today | date:'y'}}
7 | `
8 | })
9 | export class YearPipeComponent {
10 | public today: number = Date.now();
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/shared/services/alert.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { MatSnackBar } from '@angular/material';
3 |
4 | @Injectable()
5 | export class AlertService {
6 |
7 | constructor(private snackBar: MatSnackBar) {}
8 |
9 | public showToaster(msg: string): void {
10 | this.snackBar.open(msg, null, {
11 | duration: 3500,
12 | });
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/shared/services/auth-guard.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
3 |
4 | import { AuthService } from './auth.service';
5 |
6 | @Injectable()
7 | export class AuthGuardService implements CanActivate {
8 | constructor(private authService: AuthService,
9 | private router: Router) { }
10 |
11 | public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
12 | if (!this.authService.isAuthenticated()) {
13 | this.router.navigate(['/login']);
14 | return false;
15 | } else {
16 | return true;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/shared/services/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { AngularFireAuth } from '@angular/fire/auth';
3 | import { Router } from '@angular/router';
4 | import * as firebase from 'firebase';
5 |
6 | @Injectable()
7 | export class AuthService {
8 | public token: string;
9 |
10 | constructor(
11 | private router: Router,
12 | private auth: AngularFireAuth) { }
13 |
14 | public onSuccess(): void {
15 | sessionStorage.setItem('session-alive', 'true');
16 | this.token = 'some-temporary-token';
17 | this.router.navigate(['/']);
18 | console.log('AUTH: ', this.auth);
19 | }
20 |
21 | public logout(): void {
22 | sessionStorage.removeItem('session-alive');
23 | this.token = null;
24 | this.router.navigate(['/']);
25 | }
26 |
27 | public getIdToken(): string {
28 | firebase.auth().currentUser.getIdToken()
29 | .then(
30 | (token: string) => this.token = token
31 | );
32 | return this.token;
33 | }
34 |
35 | public isAuthenticated(): string {
36 | return sessionStorage.getItem('session-alive');
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/shared/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './auth-guard.service';
2 | export * from './auth.service';
3 | export * from './alert.service';
4 | export * from './user.service';
5 | export * from './window.service';
6 |
--------------------------------------------------------------------------------
/src/app/shared/services/user.service.ts:
--------------------------------------------------------------------------------
1 | import * as firebase from 'firebase';
2 | import { Injectable } from '@angular/core';
3 |
4 | import { User } from '../models';
5 | import { AlertService } from './alert.service';
6 |
7 | @Injectable()
8 | export class UserService {
9 |
10 | constructor(private alertService: AlertService) {}
11 |
12 | public saveUserInfo(uid: string, name: string, email: string): Promise {
13 | return firebase.database().ref().child('users/' + uid).set({
14 | name: name,
15 | email: email
16 | });
17 | }
18 |
19 | public updateUserInfo(uid: string, displayName: string, bio: string): Promise {
20 | return firebase.database().ref().child('users/' + uid).update({
21 | displayName: displayName,
22 | bio: bio
23 | });
24 | }
25 |
26 | public keepInTouch(email: string) {
27 | this.alertService.showToaster('Your email is saved');
28 | return firebase.database().ref().child('touch/').push({
29 | email: email
30 | });
31 | }
32 |
33 | public contactFormSend(
34 | company: string,
35 | firstname: string,
36 | lastname: string,
37 | address: string,
38 | city: string,
39 | postal: string,
40 | message: string
41 | ) {
42 | this.alertService.showToaster('This contact form is saved');
43 | return firebase.database().ref().child('contactform/').push({
44 | company: company,
45 | firstname: firstname,
46 | lastname: lastname,
47 | address: address,
48 | city: city,
49 | postal: postal,
50 | message: message
51 | });
52 | }
53 |
54 | public getUserProfileInformation(): void {
55 | const user = firebase.auth().currentUser;
56 | let name, email, photoUrl, uid, emailVerified;
57 |
58 | if (user != null) {
59 | name = user.displayName;
60 | email = user.email;
61 | photoUrl = user.photoURL;
62 | emailVerified = user.emailVerified;
63 | uid = user.uid;
64 | }
65 | }
66 |
67 | public verificationUserEmail(): Promise {
68 | return firebase.auth().currentUser.sendEmailVerification().then(() => {
69 | // Email sent.
70 | }, (error) => {
71 | // An error happened.
72 | });
73 | }
74 |
75 | public sendUserPasswordResetEmail(): Promise {
76 | return firebase.auth().sendPasswordResetEmail(firebase.auth().currentUser.email).then(() => {
77 | // Email sent.
78 | }, (error) => {
79 | // An error happened.
80 | });
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/app/shared/services/window.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable()
4 | export class WindowService {
5 |
6 | get windowRef(): Window {
7 | return window;
8 | }
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/shared/validators/email.ts:
--------------------------------------------------------------------------------
1 | import { FormControl } from '@angular/forms';
2 |
3 | export class EmailValidator {
4 |
5 | public static isValid (control: FormControl) {
6 | const RegEx = /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(control.value);
7 |
8 | if (RegEx) {
9 | return null;
10 | }
11 |
12 | return {
13 | 'invalidEmail': true
14 | };
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/shared/validators/index.ts:
--------------------------------------------------------------------------------
1 | export * from './email';
2 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/assets/css/bootstrap-social.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Social Buttons for Bootstrap
3 | *
4 | * Copyright 2013-2016 Panayiotis Lipiridis
5 | * Licensed under the MIT License
6 | *
7 | * https://github.com/lipis/bootstrap-social
8 | */
9 |
10 | .btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}
11 | .btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}
12 | .btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}
13 | .btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}
14 | .btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}
15 | .btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}
16 | .btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}
17 | .btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}
18 | .btn-social-icon>:first-child{border:none;text-align:center;width:100% !important}
19 | .btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0}
20 | .btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0}
21 | .btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0}
22 | .btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:focus,.btn-adn.focus{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}
23 | .btn-adn:hover{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}
24 | .btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:active:hover,.btn-adn.active:hover,.open>.dropdown-toggle.btn-adn:hover,.btn-adn:active:focus,.btn-adn.active:focus,.open>.dropdown-toggle.btn-adn:focus,.btn-adn:active.focus,.btn-adn.active.focus,.open>.dropdown-toggle.btn-adn.focus{color:#fff;background-color:#b94630;border-color:rgba(0,0,0,0.2)}
25 | .btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none}
26 | .btn-adn.disabled:hover,.btn-adn[disabled]:hover,fieldset[disabled] .btn-adn:hover,.btn-adn.disabled:focus,.btn-adn[disabled]:focus,fieldset[disabled] .btn-adn:focus,.btn-adn.disabled.focus,.btn-adn[disabled].focus,fieldset[disabled] .btn-adn.focus{background-color:#d87a68;border-color:rgba(0,0,0,0.2)}
27 | .btn-adn .badge{color:#d87a68;background-color:#fff}
28 | .btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:focus,.btn-bitbucket.focus{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}
29 | .btn-bitbucket:hover{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}
30 | .btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:active:hover,.btn-bitbucket.active:hover,.open>.dropdown-toggle.btn-bitbucket:hover,.btn-bitbucket:active:focus,.btn-bitbucket.active:focus,.open>.dropdown-toggle.btn-bitbucket:focus,.btn-bitbucket:active.focus,.btn-bitbucket.active.focus,.open>.dropdown-toggle.btn-bitbucket.focus{color:#fff;background-color:#0f253c;border-color:rgba(0,0,0,0.2)}
31 | .btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none}
32 | .btn-bitbucket.disabled:hover,.btn-bitbucket[disabled]:hover,fieldset[disabled] .btn-bitbucket:hover,.btn-bitbucket.disabled:focus,.btn-bitbucket[disabled]:focus,fieldset[disabled] .btn-bitbucket:focus,.btn-bitbucket.disabled.focus,.btn-bitbucket[disabled].focus,fieldset[disabled] .btn-bitbucket.focus{background-color:#205081;border-color:rgba(0,0,0,0.2)}
33 | .btn-bitbucket .badge{color:#205081;background-color:#fff}
34 | .btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:focus,.btn-dropbox.focus{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}
35 | .btn-dropbox:hover{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}
36 | .btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:active:hover,.btn-dropbox.active:hover,.open>.dropdown-toggle.btn-dropbox:hover,.btn-dropbox:active:focus,.btn-dropbox.active:focus,.open>.dropdown-toggle.btn-dropbox:focus,.btn-dropbox:active.focus,.btn-dropbox.active.focus,.open>.dropdown-toggle.btn-dropbox.focus{color:#fff;background-color:#0a568c;border-color:rgba(0,0,0,0.2)}
37 | .btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none}
38 | .btn-dropbox.disabled:hover,.btn-dropbox[disabled]:hover,fieldset[disabled] .btn-dropbox:hover,.btn-dropbox.disabled:focus,.btn-dropbox[disabled]:focus,fieldset[disabled] .btn-dropbox:focus,.btn-dropbox.disabled.focus,.btn-dropbox[disabled].focus,fieldset[disabled] .btn-dropbox.focus{background-color:#1087dd;border-color:rgba(0,0,0,0.2)}
39 | .btn-dropbox .badge{color:#1087dd;background-color:#fff}
40 | .btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:focus,.btn-facebook.focus{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}
41 | .btn-facebook:hover{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}
42 | .btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:active:hover,.btn-facebook.active:hover,.open>.dropdown-toggle.btn-facebook:hover,.btn-facebook:active:focus,.btn-facebook.active:focus,.open>.dropdown-toggle.btn-facebook:focus,.btn-facebook:active.focus,.btn-facebook.active.focus,.open>.dropdown-toggle.btn-facebook.focus{color:#fff;background-color:#23345a;border-color:rgba(0,0,0,0.2)}
43 | .btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none}
44 | .btn-facebook.disabled:hover,.btn-facebook[disabled]:hover,fieldset[disabled] .btn-facebook:hover,.btn-facebook.disabled:focus,.btn-facebook[disabled]:focus,fieldset[disabled] .btn-facebook:focus,.btn-facebook.disabled.focus,.btn-facebook[disabled].focus,fieldset[disabled] .btn-facebook.focus{background-color:#3b5998;border-color:rgba(0,0,0,0.2)}
45 | .btn-facebook .badge{color:#3b5998;background-color:#fff}
46 | .btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:focus,.btn-flickr.focus{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}
47 | .btn-flickr:hover{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}
48 | .btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:active:hover,.btn-flickr.active:hover,.open>.dropdown-toggle.btn-flickr:hover,.btn-flickr:active:focus,.btn-flickr.active:focus,.open>.dropdown-toggle.btn-flickr:focus,.btn-flickr:active.focus,.btn-flickr.active.focus,.open>.dropdown-toggle.btn-flickr.focus{color:#fff;background-color:#a80057;border-color:rgba(0,0,0,0.2)}
49 | .btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none}
50 | .btn-flickr.disabled:hover,.btn-flickr[disabled]:hover,fieldset[disabled] .btn-flickr:hover,.btn-flickr.disabled:focus,.btn-flickr[disabled]:focus,fieldset[disabled] .btn-flickr:focus,.btn-flickr.disabled.focus,.btn-flickr[disabled].focus,fieldset[disabled] .btn-flickr.focus{background-color:#ff0084;border-color:rgba(0,0,0,0.2)}
51 | .btn-flickr .badge{color:#ff0084;background-color:#fff}
52 | .btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,0.2)}.btn-foursquare:focus,.btn-foursquare.focus{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}
53 | .btn-foursquare:hover{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}
54 | .btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:active:hover,.btn-foursquare.active:hover,.open>.dropdown-toggle.btn-foursquare:hover,.btn-foursquare:active:focus,.btn-foursquare.active:focus,.open>.dropdown-toggle.btn-foursquare:focus,.btn-foursquare:active.focus,.btn-foursquare.active.focus,.open>.dropdown-toggle.btn-foursquare.focus{color:#fff;background-color:#e30742;border-color:rgba(0,0,0,0.2)}
55 | .btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none}
56 | .btn-foursquare.disabled:hover,.btn-foursquare[disabled]:hover,fieldset[disabled] .btn-foursquare:hover,.btn-foursquare.disabled:focus,.btn-foursquare[disabled]:focus,fieldset[disabled] .btn-foursquare:focus,.btn-foursquare.disabled.focus,.btn-foursquare[disabled].focus,fieldset[disabled] .btn-foursquare.focus{background-color:#f94877;border-color:rgba(0,0,0,0.2)}
57 | .btn-foursquare .badge{color:#f94877;background-color:#fff}
58 | .btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:focus,.btn-github.focus{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}
59 | .btn-github:hover{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}
60 | .btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:active:hover,.btn-github.active:hover,.open>.dropdown-toggle.btn-github:hover,.btn-github:active:focus,.btn-github.active:focus,.open>.dropdown-toggle.btn-github:focus,.btn-github:active.focus,.btn-github.active.focus,.open>.dropdown-toggle.btn-github.focus{color:#fff;background-color:#191919;border-color:rgba(0,0,0,0.2)}
61 | .btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none}
62 | .btn-github.disabled:hover,.btn-github[disabled]:hover,fieldset[disabled] .btn-github:hover,.btn-github.disabled:focus,.btn-github[disabled]:focus,fieldset[disabled] .btn-github:focus,.btn-github.disabled.focus,.btn-github[disabled].focus,fieldset[disabled] .btn-github.focus{background-color:#444;border-color:rgba(0,0,0,0.2)}
63 | .btn-github .badge{color:#444;background-color:#fff}
64 | .btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:focus,.btn-google.focus{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}
65 | .btn-google:hover{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}
66 | .btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active:hover,.btn-google.active:hover,.open>.dropdown-toggle.btn-google:hover,.btn-google:active:focus,.btn-google.active:focus,.open>.dropdown-toggle.btn-google:focus,.btn-google:active.focus,.btn-google.active.focus,.open>.dropdown-toggle.btn-google.focus{color:#fff;background-color:#a32b1c;border-color:rgba(0,0,0,0.2)}
67 | .btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none}
68 | .btn-google.disabled:hover,.btn-google[disabled]:hover,fieldset[disabled] .btn-google:hover,.btn-google.disabled:focus,.btn-google[disabled]:focus,fieldset[disabled] .btn-google:focus,.btn-google.disabled.focus,.btn-google[disabled].focus,fieldset[disabled] .btn-google.focus{background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}
69 | .btn-google .badge{color:#dd4b39;background-color:#fff}
70 | .btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:focus,.btn-instagram.focus{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}
71 | .btn-instagram:hover{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}
72 | .btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:active:hover,.btn-instagram.active:hover,.open>.dropdown-toggle.btn-instagram:hover,.btn-instagram:active:focus,.btn-instagram.active:focus,.open>.dropdown-toggle.btn-instagram:focus,.btn-instagram:active.focus,.btn-instagram.active.focus,.open>.dropdown-toggle.btn-instagram.focus{color:#fff;background-color:#26455d;border-color:rgba(0,0,0,0.2)}
73 | .btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none}
74 | .btn-instagram.disabled:hover,.btn-instagram[disabled]:hover,fieldset[disabled] .btn-instagram:hover,.btn-instagram.disabled:focus,.btn-instagram[disabled]:focus,fieldset[disabled] .btn-instagram:focus,.btn-instagram.disabled.focus,.btn-instagram[disabled].focus,fieldset[disabled] .btn-instagram.focus{background-color:#3f729b;border-color:rgba(0,0,0,0.2)}
75 | .btn-instagram .badge{color:#3f729b;background-color:#fff}
76 | .btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:focus,.btn-linkedin.focus{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}
77 | .btn-linkedin:hover{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}
78 | .btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:active:hover,.btn-linkedin.active:hover,.open>.dropdown-toggle.btn-linkedin:hover,.btn-linkedin:active:focus,.btn-linkedin.active:focus,.open>.dropdown-toggle.btn-linkedin:focus,.btn-linkedin:active.focus,.btn-linkedin.active.focus,.open>.dropdown-toggle.btn-linkedin.focus{color:#fff;background-color:#00405f;border-color:rgba(0,0,0,0.2)}
79 | .btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none}
80 | .btn-linkedin.disabled:hover,.btn-linkedin[disabled]:hover,fieldset[disabled] .btn-linkedin:hover,.btn-linkedin.disabled:focus,.btn-linkedin[disabled]:focus,fieldset[disabled] .btn-linkedin:focus,.btn-linkedin.disabled.focus,.btn-linkedin[disabled].focus,fieldset[disabled] .btn-linkedin.focus{background-color:#007bb6;border-color:rgba(0,0,0,0.2)}
81 | .btn-linkedin .badge{color:#007bb6;background-color:#fff}
82 | .btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:focus,.btn-microsoft.focus{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}
83 | .btn-microsoft:hover{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}
84 | .btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:active:hover,.btn-microsoft.active:hover,.open>.dropdown-toggle.btn-microsoft:hover,.btn-microsoft:active:focus,.btn-microsoft.active:focus,.open>.dropdown-toggle.btn-microsoft:focus,.btn-microsoft:active.focus,.btn-microsoft.active.focus,.open>.dropdown-toggle.btn-microsoft.focus{color:#fff;background-color:#0f4bac;border-color:rgba(0,0,0,0.2)}
85 | .btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none}
86 | .btn-microsoft.disabled:hover,.btn-microsoft[disabled]:hover,fieldset[disabled] .btn-microsoft:hover,.btn-microsoft.disabled:focus,.btn-microsoft[disabled]:focus,fieldset[disabled] .btn-microsoft:focus,.btn-microsoft.disabled.focus,.btn-microsoft[disabled].focus,fieldset[disabled] .btn-microsoft.focus{background-color:#2672ec;border-color:rgba(0,0,0,0.2)}
87 | .btn-microsoft .badge{color:#2672ec;background-color:#fff}
88 | .btn-odnoklassniki{color:#fff;background-color:#f4731c;border-color:rgba(0,0,0,0.2)}.btn-odnoklassniki:focus,.btn-odnoklassniki.focus{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)}
89 | .btn-odnoklassniki:hover{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)}
90 | .btn-odnoklassniki:active,.btn-odnoklassniki.active,.open>.dropdown-toggle.btn-odnoklassniki{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)}.btn-odnoklassniki:active:hover,.btn-odnoklassniki.active:hover,.open>.dropdown-toggle.btn-odnoklassniki:hover,.btn-odnoklassniki:active:focus,.btn-odnoklassniki.active:focus,.open>.dropdown-toggle.btn-odnoklassniki:focus,.btn-odnoklassniki:active.focus,.btn-odnoklassniki.active.focus,.open>.dropdown-toggle.btn-odnoklassniki.focus{color:#fff;background-color:#b14c09;border-color:rgba(0,0,0,0.2)}
91 | .btn-odnoklassniki:active,.btn-odnoklassniki.active,.open>.dropdown-toggle.btn-odnoklassniki{background-image:none}
92 | .btn-odnoklassniki.disabled:hover,.btn-odnoklassniki[disabled]:hover,fieldset[disabled] .btn-odnoklassniki:hover,.btn-odnoklassniki.disabled:focus,.btn-odnoklassniki[disabled]:focus,fieldset[disabled] .btn-odnoklassniki:focus,.btn-odnoklassniki.disabled.focus,.btn-odnoklassniki[disabled].focus,fieldset[disabled] .btn-odnoklassniki.focus{background-color:#f4731c;border-color:rgba(0,0,0,0.2)}
93 | .btn-odnoklassniki .badge{color:#f4731c;background-color:#fff}
94 | .btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:focus,.btn-openid.focus{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}
95 | .btn-openid:hover{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}
96 | .btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active:hover,.btn-openid.active:hover,.open>.dropdown-toggle.btn-openid:hover,.btn-openid:active:focus,.btn-openid.active:focus,.open>.dropdown-toggle.btn-openid:focus,.btn-openid:active.focus,.btn-openid.active.focus,.open>.dropdown-toggle.btn-openid.focus{color:#fff;background-color:#b86607;border-color:rgba(0,0,0,0.2)}
97 | .btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none}
98 | .btn-openid.disabled:hover,.btn-openid[disabled]:hover,fieldset[disabled] .btn-openid:hover,.btn-openid.disabled:focus,.btn-openid[disabled]:focus,fieldset[disabled] .btn-openid:focus,.btn-openid.disabled.focus,.btn-openid[disabled].focus,fieldset[disabled] .btn-openid.focus{background-color:#f7931e;border-color:rgba(0,0,0,0.2)}
99 | .btn-openid .badge{color:#f7931e;background-color:#fff}
100 | .btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,0.2)}.btn-pinterest:focus,.btn-pinterest.focus{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}
101 | .btn-pinterest:hover{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}
102 | .btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:active:hover,.btn-pinterest.active:hover,.open>.dropdown-toggle.btn-pinterest:hover,.btn-pinterest:active:focus,.btn-pinterest.active:focus,.open>.dropdown-toggle.btn-pinterest:focus,.btn-pinterest:active.focus,.btn-pinterest.active.focus,.open>.dropdown-toggle.btn-pinterest.focus{color:#fff;background-color:#801419;border-color:rgba(0,0,0,0.2)}
103 | .btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none}
104 | .btn-pinterest.disabled:hover,.btn-pinterest[disabled]:hover,fieldset[disabled] .btn-pinterest:hover,.btn-pinterest.disabled:focus,.btn-pinterest[disabled]:focus,fieldset[disabled] .btn-pinterest:focus,.btn-pinterest.disabled.focus,.btn-pinterest[disabled].focus,fieldset[disabled] .btn-pinterest.focus{background-color:#cb2027;border-color:rgba(0,0,0,0.2)}
105 | .btn-pinterest .badge{color:#cb2027;background-color:#fff}
106 | .btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:focus,.btn-reddit.focus{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}
107 | .btn-reddit:hover{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}
108 | .btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:active:hover,.btn-reddit.active:hover,.open>.dropdown-toggle.btn-reddit:hover,.btn-reddit:active:focus,.btn-reddit.active:focus,.open>.dropdown-toggle.btn-reddit:focus,.btn-reddit:active.focus,.btn-reddit.active.focus,.open>.dropdown-toggle.btn-reddit.focus{color:#000;background-color:#98ccff;border-color:rgba(0,0,0,0.2)}
109 | .btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none}
110 | .btn-reddit.disabled:hover,.btn-reddit[disabled]:hover,fieldset[disabled] .btn-reddit:hover,.btn-reddit.disabled:focus,.btn-reddit[disabled]:focus,fieldset[disabled] .btn-reddit:focus,.btn-reddit.disabled.focus,.btn-reddit[disabled].focus,fieldset[disabled] .btn-reddit.focus{background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}
111 | .btn-reddit .badge{color:#eff7ff;background-color:#000}
112 | .btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:focus,.btn-soundcloud.focus{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}
113 | .btn-soundcloud:hover{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}
114 | .btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:active:hover,.btn-soundcloud.active:hover,.open>.dropdown-toggle.btn-soundcloud:hover,.btn-soundcloud:active:focus,.btn-soundcloud.active:focus,.open>.dropdown-toggle.btn-soundcloud:focus,.btn-soundcloud:active.focus,.btn-soundcloud.active.focus,.open>.dropdown-toggle.btn-soundcloud.focus{color:#fff;background-color:#a83800;border-color:rgba(0,0,0,0.2)}
115 | .btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none}
116 | .btn-soundcloud.disabled:hover,.btn-soundcloud[disabled]:hover,fieldset[disabled] .btn-soundcloud:hover,.btn-soundcloud.disabled:focus,.btn-soundcloud[disabled]:focus,fieldset[disabled] .btn-soundcloud:focus,.btn-soundcloud.disabled.focus,.btn-soundcloud[disabled].focus,fieldset[disabled] .btn-soundcloud.focus{background-color:#f50;border-color:rgba(0,0,0,0.2)}
117 | .btn-soundcloud .badge{color:#f50;background-color:#fff}
118 | .btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:focus,.btn-tumblr.focus{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}
119 | .btn-tumblr:hover{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}
120 | .btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:active:hover,.btn-tumblr.active:hover,.open>.dropdown-toggle.btn-tumblr:hover,.btn-tumblr:active:focus,.btn-tumblr.active:focus,.open>.dropdown-toggle.btn-tumblr:focus,.btn-tumblr:active.focus,.btn-tumblr.active.focus,.open>.dropdown-toggle.btn-tumblr.focus{color:#fff;background-color:#111c26;border-color:rgba(0,0,0,0.2)}
121 | .btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none}
122 | .btn-tumblr.disabled:hover,.btn-tumblr[disabled]:hover,fieldset[disabled] .btn-tumblr:hover,.btn-tumblr.disabled:focus,.btn-tumblr[disabled]:focus,fieldset[disabled] .btn-tumblr:focus,.btn-tumblr.disabled.focus,.btn-tumblr[disabled].focus,fieldset[disabled] .btn-tumblr.focus{background-color:#2c4762;border-color:rgba(0,0,0,0.2)}
123 | .btn-tumblr .badge{color:#2c4762;background-color:#fff}
124 | .btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:focus,.btn-twitter.focus{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}
125 | .btn-twitter:hover{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}
126 | .btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:active:hover,.btn-twitter.active:hover,.open>.dropdown-toggle.btn-twitter:hover,.btn-twitter:active:focus,.btn-twitter.active:focus,.open>.dropdown-toggle.btn-twitter:focus,.btn-twitter:active.focus,.btn-twitter.active.focus,.open>.dropdown-toggle.btn-twitter.focus{color:#fff;background-color:#1583d7;border-color:rgba(0,0,0,0.2)}
127 | .btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none}
128 | .btn-twitter.disabled:hover,.btn-twitter[disabled]:hover,fieldset[disabled] .btn-twitter:hover,.btn-twitter.disabled:focus,.btn-twitter[disabled]:focus,fieldset[disabled] .btn-twitter:focus,.btn-twitter.disabled.focus,.btn-twitter[disabled].focus,fieldset[disabled] .btn-twitter.focus{background-color:#55acee;border-color:rgba(0,0,0,0.2)}
129 | .btn-twitter .badge{color:#55acee;background-color:#fff}
130 | .btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:focus,.btn-vimeo.focus{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}
131 | .btn-vimeo:hover{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}
132 | .btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:active:hover,.btn-vimeo.active:hover,.open>.dropdown-toggle.btn-vimeo:hover,.btn-vimeo:active:focus,.btn-vimeo.active:focus,.open>.dropdown-toggle.btn-vimeo:focus,.btn-vimeo:active.focus,.btn-vimeo.active.focus,.open>.dropdown-toggle.btn-vimeo.focus{color:#fff;background-color:#0f7b9f;border-color:rgba(0,0,0,0.2)}
133 | .btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none}
134 | .btn-vimeo.disabled:hover,.btn-vimeo[disabled]:hover,fieldset[disabled] .btn-vimeo:hover,.btn-vimeo.disabled:focus,.btn-vimeo[disabled]:focus,fieldset[disabled] .btn-vimeo:focus,.btn-vimeo.disabled.focus,.btn-vimeo[disabled].focus,fieldset[disabled] .btn-vimeo.focus{background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}
135 | .btn-vimeo .badge{color:#1ab7ea;background-color:#fff}
136 | .btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:focus,.btn-vk.focus{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}
137 | .btn-vk:hover{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}
138 | .btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:active:hover,.btn-vk.active:hover,.open>.dropdown-toggle.btn-vk:hover,.btn-vk:active:focus,.btn-vk.active:focus,.open>.dropdown-toggle.btn-vk:focus,.btn-vk:active.focus,.btn-vk.active.focus,.open>.dropdown-toggle.btn-vk.focus{color:#fff;background-color:#3a526b;border-color:rgba(0,0,0,0.2)}
139 | .btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none}
140 | .btn-vk.disabled:hover,.btn-vk[disabled]:hover,fieldset[disabled] .btn-vk:hover,.btn-vk.disabled:focus,.btn-vk[disabled]:focus,fieldset[disabled] .btn-vk:focus,.btn-vk.disabled.focus,.btn-vk[disabled].focus,fieldset[disabled] .btn-vk.focus{background-color:#587ea3;border-color:rgba(0,0,0,0.2)}
141 | .btn-vk .badge{color:#587ea3;background-color:#fff}
142 | .btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:focus,.btn-yahoo.focus{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}
143 | .btn-yahoo:hover{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}
144 | .btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:active:hover,.btn-yahoo.active:hover,.open>.dropdown-toggle.btn-yahoo:hover,.btn-yahoo:active:focus,.btn-yahoo.active:focus,.open>.dropdown-toggle.btn-yahoo:focus,.btn-yahoo:active.focus,.btn-yahoo.active.focus,.open>.dropdown-toggle.btn-yahoo.focus{color:#fff;background-color:#39074e;border-color:rgba(0,0,0,0.2)}
145 | .btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none}
146 | .btn-yahoo.disabled:hover,.btn-yahoo[disabled]:hover,fieldset[disabled] .btn-yahoo:hover,.btn-yahoo.disabled:focus,.btn-yahoo[disabled]:focus,fieldset[disabled] .btn-yahoo:focus,.btn-yahoo.disabled.focus,.btn-yahoo[disabled].focus,fieldset[disabled] .btn-yahoo.focus{background-color:#720e9e;border-color:rgba(0,0,0,0.2)}
147 | .btn-yahoo .badge{color:#720e9e;background-color:#fff}
148 |
--------------------------------------------------------------------------------
/src/assets/img/angular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/angular.png
--------------------------------------------------------------------------------
/src/assets/img/angular2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/angular2.png
--------------------------------------------------------------------------------
/src/assets/img/mb-bg-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/mb-bg-01.png
--------------------------------------------------------------------------------
/src/assets/img/mb-bg-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/mb-bg-02.png
--------------------------------------------------------------------------------
/src/assets/img/mb-bg-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/mb-bg-03.png
--------------------------------------------------------------------------------
/src/assets/img/mb-bg-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/mb-bg-04.png
--------------------------------------------------------------------------------
/src/assets/img/mb-bg-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/mb-bg-05.png
--------------------------------------------------------------------------------
/src/assets/img/mb-bg-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/mb-bg-06.png
--------------------------------------------------------------------------------
/src/assets/img/mountsthelens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/assets/img/mountsthelens.png
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `angular-cli.json`.
5 | // import 'zone.js/dist/zone-error';
6 |
7 | export const environment = {
8 | production: false
9 | };
10 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeroenouw/AngularMaterialFirebase/9e2ff9aedfa044bacb9f34f7846c7e0fbc8173bd/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular 8 | Material Design | Firebase
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | App is loading...
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 | import { enableProdMode } from '@angular/core';
3 | import { environment } from './environments/environment';
4 | import { AppModule } from './app/app.module';
5 | import 'hammerjs';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule, {
12 | preserveWhitespaces: false
13 | });
14 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | // This file includes polyfills needed by Angular and is loaded before the app.
2 | // You can add your own extra polyfills to this file.
3 | // import 'core-js/es6/symbol';
4 | // import 'core-js/es6/object';
5 | // import 'core-js/es6/function';
6 | // import 'core-js/es6/parse-int';
7 | // import 'core-js/es6/parse-float';
8 | // import 'core-js/es6/number';
9 | // import 'core-js/es6/math';
10 | // import 'core-js/es6/string';
11 | // import 'core-js/es6/date';
12 | // import 'core-js/es6/array';
13 | // import 'core-js/es6/regexp';
14 | // import 'core-js/es6/map';
15 | // import 'core-js/es6/set';
16 | // import 'core-js/es6/reflect';
17 |
18 | // import 'core-js/es7/reflect';
19 | import 'zone.js/dist/zone';
20 |
21 | // If you need to support the browsers/features below, uncomment the import
22 | // and run `npm install import-name-here';
23 | // Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
24 |
25 | // Needed for: IE9
26 | // import 'classlist.js';
27 |
28 | // Animations
29 | // Needed for: All but Chrome and Firefox, Not supported in IE9
30 | // import 'web-animations-js';
31 |
32 | // Date, currency, decimal and percent pipes
33 | // Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
34 | // import 'intl';
35 |
36 | // NgClass on SVG elements
37 | // Needed for: IE10, IE11
38 | // import 'classlist.js';
39 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import '~@angular/material/prebuilt-themes/deeppurple-amber';
3 |
4 | body {
5 | background-color: #f7f7f7;
6 | }
7 |
8 | /* Tablet format and lower */
9 | @media only screen and (max-width: 1024px) {
10 |
11 | }
12 |
13 | /* Mobile format and lower */
14 | @media only screen and (max-width: 768px) {
15 |
16 | }
17 |
18 | /* Mobile format landscape (wide) */
19 | @media only screen and (min-width: 320px) and (max-width: 480px) {
20 |
21 | }
22 |
23 | /* Mobile format portrait (small) */
24 | @media only screen and (max-width: 320px) {
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare var __karma__: any;
17 | declare var require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | const context = require.context('./', true, /\.spec\.ts$/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": [],
6 | "forceConsistentCasingInFileNames": false
7 | },
8 | "exclude": [
9 | "src/test.ts",
10 | "**/*.spec.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "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 |
--------------------------------------------------------------------------------
/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "custom",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "custom",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "module": "esnext",
6 | "outDir": "./dist/out-tsc",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "forceConsistentCasingInFileNames": false,
13 | "target": "es2015",
14 | "typeRoots": [
15 | "node_modules/@types"
16 | ],
17 | "lib": [
18 | "es2017",
19 | "dom"
20 | ],
21 | "paths": {
22 | "@shared/*": [
23 | "src/app/shared/*"
24 | ],
25 | "@shared": [
26 | "src/app/shared"
27 | ]
28 | }
29 | },
30 | "angularCompilerOptions": {
31 | "enableIvy": false
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-shadowed-variable": false,
69 | "no-string-literal": false,
70 | "no-string-throw": true,
71 | "no-switch-case-fall-through": true,
72 | "no-trailing-whitespace": true,
73 | "no-unnecessary-initializer": true,
74 | "no-unused-expression": true,
75 | "no-use-before-declare": true,
76 | "no-var-keyword": true,
77 | "object-literal-sort-keys": false,
78 | "one-line": [
79 | true,
80 | "check-open-brace",
81 | "check-catch",
82 | "check-else",
83 | "check-whitespace"
84 | ],
85 | "prefer-const": true,
86 | "quotemark": [
87 | true,
88 | "single"
89 | ],
90 | "radix": true,
91 | "semicolon": [
92 | true,
93 | "always"
94 | ],
95 | "triple-equals": [
96 | true,
97 | "allow-null-check"
98 | ],
99 | "typedef-whitespace": [
100 | true,
101 | {
102 | "call-signature": "nospace",
103 | "index-signature": "nospace",
104 | "parameter": "nospace",
105 | "property-declaration": "nospace",
106 | "variable-declaration": "nospace"
107 | }
108 | ],
109 | "unified-signatures": true,
110 | "variable-name": false,
111 | "whitespace": [
112 | true,
113 | "check-branch",
114 | "check-decl",
115 | "check-operator",
116 | "check-separator",
117 | "check-type"
118 | ],
119 | "no-output-on-prefix": false,
120 | "no-inputs-metadata-property": true,
121 | "no-outputs-metadata-property": true,
122 | "no-host-metadata-property": true,
123 | "no-input-rename": true,
124 | "no-output-rename": true,
125 | "use-lifecycle-interface": true,
126 | "use-pipe-transform-interface": true,
127 | "component-class-suffix": true,
128 | "directive-class-suffix": true
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/typings.json:
--------------------------------------------------------------------------------
1 | {
2 | "globalDependencies": {
3 | "hammerjs": "registry:dt/hammerjs#2.0.8+20161224203613"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------