| import { AppRoutingModule } from './app-routing.module'; | import { AppRoutingModule } from './app-routing.module'; | ||||
| import { AppComponent } from './app.component'; | import { AppComponent } from './app.component'; | ||||
| import { HeaderComponent } from './misc/header/header.component'; | |||||
| import { FooterComponent } from './misc/footer/footer.component'; | import { FooterComponent } from './misc/footer/footer.component'; | ||||
| import { NewsDashboardComponent } from './news/news-dashboard/news-dashboard.component'; | import { NewsDashboardComponent } from './news/news-dashboard/news-dashboard.component'; | ||||
| import { HttpClientModule } from '@angular/common/http'; | import { HttpClientModule } from '@angular/common/http'; | ||||
| @NgModule({ | @NgModule({ | ||||
| declarations: [ | declarations: [ | ||||
| AppComponent, | AppComponent, | ||||
| HeaderComponent, | |||||
| FooterComponent, | FooterComponent, | ||||
| NewsDashboardComponent, | NewsDashboardComponent, | ||||
| TopNewsDashboardComponent, | TopNewsDashboardComponent, |
| @import "../../../variables.scss"; | |||||
| .header { | |||||
| color: white; | |||||
| background: transparent; | |||||
| margin: 0px; | |||||
| font-size: 30px; | |||||
| padding: 1em 1em; | |||||
| } |
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { HeaderComponent } from './header.component'; | |||||
| describe('HeaderComponent', () => { | |||||
| let component: HeaderComponent; | |||||
| let fixture: ComponentFixture<HeaderComponent>; | |||||
| beforeEach(async(() => { | |||||
| TestBed.configureTestingModule({ | |||||
| declarations: [ HeaderComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| })); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(HeaderComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import { Component, OnInit } from '@angular/core'; | |||||
| import { faNewspaper } from '@fortawesome/free-solid-svg-icons'; | |||||
| @Component({ | |||||
| selector: 'app-header', | |||||
| templateUrl: './header.component.html', | |||||
| styleUrls: ['./header.component.scss'] | |||||
| }) | |||||
| export class HeaderComponent implements OnInit { | |||||
| faNewspaper = faNewspaper; | |||||
| constructor() { } | |||||
| ngOnInit(): void { | |||||
| } | |||||
| } |
| @-webkit-keyframes slide-in-right { | @-webkit-keyframes slide-in-right { | ||||
| 0% { | |||||
| from { | |||||
| -webkit-transform: translateX(1000px); | -webkit-transform: translateX(1000px); | ||||
| transform: translateX(1000px); | transform: translateX(1000px); | ||||
| } | } | ||||
| 100% { | |||||
| to { | |||||
| visibility: visible; | |||||
| -webkit-transform: translateX(0); | -webkit-transform: translateX(0); | ||||
| transform: translateX(0); | transform: translateX(0); | ||||
| } | } | ||||
| } | } | ||||
| @keyframes slide-in-right { | @keyframes slide-in-right { | ||||
| 0% { | |||||
| from { | |||||
| -webkit-transform: translateX(1000px); | -webkit-transform: translateX(1000px); | ||||
| transform: translateX(1000px); | transform: translateX(1000px); | ||||
| } | } | ||||
| 100% { | |||||
| to { | |||||
| visibility: visible; | |||||
| -webkit-transform: translateX(0); | -webkit-transform: translateX(0); | ||||
| transform: translateX(0); | transform: translateX(0); | ||||
| } | } | ||||
| } | } | ||||
| @-webkit-keyframes slide-in-left { | @-webkit-keyframes slide-in-left { | ||||
| 0% { | |||||
| from { | |||||
| -webkit-transform: translateX(-1000px); | -webkit-transform: translateX(-1000px); | ||||
| transform: translateX(-1000px); | transform: translateX(-1000px); | ||||
| } | } | ||||
| 100% { | |||||
| to { | |||||
| visibility: visible; | |||||
| -webkit-transform: translateX(0); | -webkit-transform: translateX(0); | ||||
| transform: translateX(0); | transform: translateX(0); | ||||
| } | } | ||||
| } | } | ||||
| @keyframes slide-in-left { | @keyframes slide-in-left { | ||||
| 0% { | |||||
| from { | |||||
| -webkit-transform: translateX(-1000px); | -webkit-transform: translateX(-1000px); | ||||
| transform: translateX(-1000px); | transform: translateX(-1000px); | ||||
| } | } | ||||
| 100% { | |||||
| to { | |||||
| visibility: visible; | |||||
| -webkit-transform: translateX(0); | -webkit-transform: translateX(0); | ||||
| transform: translateX(0); | transform: translateX(0); | ||||
| } | } | ||||
| } | } | ||||
| @-webkit-keyframes slide-out-right { | @-webkit-keyframes slide-out-right { | ||||
| 0% { | |||||
| from { | |||||
| -webkit-transform: translateX(0); | -webkit-transform: translateX(0); | ||||
| transform: translateX(0); | transform: translateX(0); | ||||
| opacity: 1; | |||||
| } | } | ||||
| 100% { | |||||
| overflow: hidden; | |||||
| width: 100%; | |||||
| 99% { | |||||
| opacity: 0 !important; | |||||
| width: 100% !important; | |||||
| height: 100% !important; | |||||
| } | |||||
| to { | |||||
| height: 0px !important; | |||||
| width: 0px; | |||||
| opacity: 0 !important; | |||||
| display: none !important; | |||||
| visibility: hidden !important; | |||||
| -webkit-transform: translateX(1000px); | -webkit-transform: translateX(1000px); | ||||
| transform: translateX(1000px); | transform: translateX(1000px); | ||||
| overflow: hidden; | |||||
| opacity: 0; | |||||
| width: 0px; | |||||
| height: 0px; | |||||
| } | } | ||||
| } | } | ||||
| @keyframes slide-out-right { | @keyframes slide-out-right { | ||||
| 0% { | |||||
| from { | |||||
| -webkit-transform: translateX(0); | -webkit-transform: translateX(0); | ||||
| transform: translateX(0); | transform: translateX(0); | ||||
| opacity: 1; | |||||
| } | } | ||||
| 100% { | |||||
| overflow: hidden; | |||||
| width: 100%; | |||||
| 99% { | |||||
| opacity: 0 !important; | |||||
| width: 100% !important; | |||||
| height: 100% !important; | |||||
| } | |||||
| to { | |||||
| height: 0px; | |||||
| width: 0px; | |||||
| opacity: 0 !important; | |||||
| display: none !important; | |||||
| visibility: hidden !important; | |||||
| -webkit-transform: translateX(1000px); | -webkit-transform: translateX(1000px); | ||||
| transform: translateX(1000px); | transform: translateX(1000px); | ||||
| overflow: hidden; | |||||
| opacity: 0; | |||||
| width: 0px; | |||||
| height: 0px; | |||||
| } | } | ||||
| } | } | ||||
| @-webkit-keyframes slide-out-left { | @-webkit-keyframes slide-out-left { | ||||
| 0% { | |||||
| from { | |||||
| -webkit-transform: translateX(0); | -webkit-transform: translateX(0); | ||||
| transform: translateX(0); | transform: translateX(0); | ||||
| opacity: 1; | |||||
| } | } | ||||
| 100% { | |||||
| overflow: hidden; | |||||
| width: 100%; | |||||
| 99% { | |||||
| opacity: 0 !important; | |||||
| width: 100% !important; | |||||
| height: 100% !important; | |||||
| } | |||||
| to { | |||||
| height: 0px; | |||||
| width: 0px; | |||||
| opacity: 0 !important; | |||||
| display: none !important; | |||||
| visibility: hidden !important; | |||||
| -webkit-transform: translateX(-1000px); | -webkit-transform: translateX(-1000px); | ||||
| transform: translateX(-1000px); | transform: translateX(-1000px); | ||||
| overflow: hidden; | |||||
| opacity: 0; | |||||
| width: 0px; | |||||
| height: 0px; | |||||
| } | } | ||||
| } | } | ||||
| @keyframes slide-out-left { | @keyframes slide-out-left { | ||||
| 0% { | |||||
| from { | |||||
| -webkit-transform: translateX(0); | -webkit-transform: translateX(0); | ||||
| transform: translateX(0); | transform: translateX(0); | ||||
| opacity: 1; | |||||
| } | } | ||||
| 100% { | |||||
| overflow: hidden; | |||||
| width: 100%; | |||||
| 99% { | |||||
| opacity: 0 !important; | |||||
| width: 100% !important; | |||||
| height: 100% !important; | |||||
| } | |||||
| to { | |||||
| height: 0px; | |||||
| width: 0px; | |||||
| opacity: 0 !important; | |||||
| display: none !important; | |||||
| visibility: hidden !important; | |||||
| -webkit-transform: translateX(-1000px); | -webkit-transform: translateX(-1000px); | ||||
| transform: translateX(-1000px); | transform: translateX(-1000px); | ||||
| opacity: 0; | |||||
| width: 0px; | |||||
| height: 0px; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| .slide-in-left { | .slide-in-left { | ||||
| visibility: visible; | |||||
| -webkit-animation: slide-in-left 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; | |||||
| animation: slide-in-left 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; | |||||
| -webkit-animation: slide-in-left 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; | |||||
| animation: slide-in-left 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; | |||||
| } | } | ||||
| .slide-in-right { | .slide-in-right { | ||||
| visibility: visible; | |||||
| -webkit-animation: slide-in-right 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; | |||||
| animation: slide-in-right 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; | |||||
| -webkit-animation: slide-in-right 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; | |||||
| animation: slide-in-right 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; | |||||
| } | } | ||||
| .slide-out-right { | .slide-out-right { | ||||
| -webkit-animation: slide-out-right 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) both; | |||||
| animation: slide-out-right 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) both; | |||||
| -webkit-animation: slide-out-right 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards; | |||||
| animation: slide-out-right 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards; | |||||
| } | } | ||||
| .slide-out-left { | .slide-out-left { | ||||
| -webkit-animation: slide-out-left 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) both; | |||||
| animation: slide-out-left 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) both; | |||||
| -webkit-animation: slide-out-left 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards; | |||||
| animation: slide-out-left 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards; | |||||
| } | } | ||||
| .inactive { | .inactive { | ||||
| display: none; | |||||
| display: none !important; | |||||
| visibility: hidden !important !important !important; | |||||
| } | } | ||||
| .pane { | .pane { | ||||
| width: 100%; | width: 100%; | ||||
| } | } |
| padding: 1em; | padding: 1em; | ||||
| } | } | ||||
| .title { | |||||
| justify-self: flex-start; | |||||
| align-items: self-start; | |||||
| } | |||||
| .tab-title:active, | |||||
| .active { | |||||
| opacity: 1; | |||||
| } | |||||
| .tab-title:hover { | |||||
| opacity: 0.8; | |||||
| } | |||||
| .tab-container { | .tab-container { | ||||
| border-radius: $border-radius; | border-radius: $border-radius; | ||||
| background: white; | background: white; | ||||
| width: 100%; | |||||
| min-height: 60rem; | |||||
| } | } | ||||
| .header-icon { | .header-icon { | ||||
| padding: 1em; | padding: 1em; | ||||
| } | } | ||||
| .tab-title:active, | |||||
| .active { | |||||
| opacity: 1; | |||||
| } | |||||
| .tab-title:hover { | |||||
| opacity: 0.8; | |||||
| } | |||||
| .header { | .header { | ||||
| justify-content: space-between; | justify-content: space-between; | ||||
| align-items: spac; | |||||
| height: 3em; | height: 3em; | ||||
| flex-direction: row nowrap; | |||||
| flex-direction: row; | |||||
| font-size: $header-font-size; | font-size: $header-font-size; | ||||
| color: white; | color: white; | ||||
| background: linear-gradient(48deg, $detail-color, $secondary-color, $primary-color); | background: linear-gradient(48deg, $detail-color, $secondary-color, $primary-color); | ||||
| } | } | ||||
| @media(max-width: 600px) { | |||||
| .header { | |||||
| font-size: $header-font-size / 2; | |||||
| } | |||||
| } |
| 'Entertainment' = 'entertainment', | 'Entertainment' = 'entertainment', | ||||
| 'General' = 'general', | 'General' = 'general', | ||||
| 'Health' = 'health', | 'Health' = 'health', | ||||
| 'Scince' = 'science', | |||||
| 'Science' = 'science', | |||||
| 'Sports' = 'sports', | 'Sports' = 'sports', | ||||
| 'Technology' = 'technology', | 'Technology' = 'technology', | ||||
| } | } |
| <div class="flex-container wrap"> | |||||
| <div class="title flex-container"> | |||||
| <p>News</p> | |||||
| </div> | |||||
| <div class="flex-container filter-bar"> | |||||
| <div class="filter"> | |||||
| <label for="search">Search: </label> | |||||
| <input | |||||
| (change)="search()" | |||||
| name="search" | |||||
| id="search" | |||||
| [(ngModel)]="searchDto.q" | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| <div class="flex-container wrap" *ngIf="news?.length; else notingFound"> | |||||
| <div class="flex-item" *ngFor="let article of news"> | <div class="flex-item" *ngFor="let article of news"> | ||||
| <app-news-teaser [article]="article"></app-news-teaser> | <app-news-teaser [article]="article"></app-news-teaser> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <ng-template #notingFound> | |||||
| <div class="flex-container not-found"> | |||||
| noting here | |||||
| </div> | |||||
| </ng-template> |
| @import "../../../variables.scss"; | |||||
| .filter-bar { | |||||
| height: 5em; | |||||
| margin: 2em; | |||||
| display: flex; | |||||
| flex-flow: row; | |||||
| justify-content: space-evenly; | |||||
| align-items: center; | |||||
| border: 1px solid $border-color; | |||||
| } | |||||
| .filter { | |||||
| height: 2em; | |||||
| } | |||||
| .title { | |||||
| padding-top: 1em; | |||||
| } | |||||
| .flag { | |||||
| height: 1em; | |||||
| padding-left: 0.5em; | |||||
| align-self: center; | |||||
| } | |||||
| .not-found { | |||||
| width: auto; | |||||
| height: 100%; | |||||
| padding: 5em; | |||||
| } | |||||
| @media (max-width: 600px) { | |||||
| .filter-bar { | |||||
| flex-flow: column; | |||||
| justify-content: center; | |||||
| } | |||||
| } | |||||
| import { NewsService } from '../../service/news.service'; | import { NewsService } from '../../service/news.service'; | ||||
| import { Article } from '../../models/model/article'; | import { Article } from '../../models/model/article'; | ||||
| import { apiKey } from '../../../environments/.api-key'; | import { apiKey } from '../../../environments/.api-key'; | ||||
| import { NewsSearchDto } from '../../models/dto/news-search.dto'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-all-news-dashboard', | selector: 'app-all-news-dashboard', | ||||
| }) | }) | ||||
| export class AllNewsDashboardComponent implements OnInit { | export class AllNewsDashboardComponent implements OnInit { | ||||
| searchDto: NewsSearchDto = {apiKey}; | |||||
| news: Article[]; | news: Article[]; | ||||
| constructor(protected newsService: NewsService) { } | constructor(protected newsService: NewsService) { } | ||||
| search(): void { | |||||
| this.newsService.searchAllNews(this.searchDto).subscribe(response => { | |||||
| this.news = response.articles; | |||||
| }); | |||||
| } | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| // this.newsService.searchAllNews({ | |||||
| // apiKey | |||||
| // }).subscribe(response => { | |||||
| // this.news = response.articles; | |||||
| // }); | |||||
| } | } | ||||
| <app-tabs> | <app-tabs> | ||||
| <app-tab style="width: auto" title="Top News"> | |||||
| <app-tab title="Top News"> | |||||
| <app-top-news-dashboard></app-top-news-dashboard> | <app-top-news-dashboard></app-top-news-dashboard> | ||||
| </app-tab> | </app-tab> | ||||
| <app-tab style="width: auto" title="All News"> | |||||
| <app-tab title="All News"> | |||||
| <app-all-news-dashboard></app-all-news-dashboard> | <app-all-news-dashboard></app-all-news-dashboard> | ||||
| </app-tab> | </app-tab> | ||||
| </app-tabs> | </app-tabs> |
| <div | <div | ||||
| (click)="open()" | (click)="open()" | ||||
| class="teaser" | class="teaser" | ||||
| *ngIf="article.urlToImage && article.content" | |||||
| *ngIf="article.urlToImage && article.description && article.url" | |||||
| > | > | ||||
| <div class="teaser-header"> | <div class="teaser-header"> | ||||
| <div class="date" >{{ article.publishedAt | date:"dd-MM-yyyy" }}</div> | |||||
| <div class="teaser-title">{{ article.title }}</div> | <div class="teaser-title">{{ article.title }}</div> | ||||
| <div class="teaser-img"> | <div class="teaser-img"> | ||||
| <img | <img | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="teaser-content" *ngIf="article.description"> | |||||
| {{ article.description }} ... <a [href]="article.url"> Read</a> | |||||
| <div class="teaser-content"> | |||||
| <ng-container *ngIf="article.description"> | |||||
| {{ article.description }} ... <a [href]="article.url"> Read</a> | |||||
| </ng-container> | |||||
| </div> | </div> | ||||
| </div> | </div> |
| $teaser-background: linear-gradient(48deg, rgb(223, 217, 217), white); | $teaser-background: linear-gradient(48deg, rgb(223, 217, 217), white); | ||||
| .teaser-header { | .teaser-header { | ||||
| min-height: 10em; | |||||
| flex-direction: column; | flex-direction: column; | ||||
| max-width: 40em; | |||||
| > div.teaser-img { | > div.teaser-img { | ||||
| border-top-left-radius: $teaser-border-radius; | border-top-left-radius: $teaser-border-radius; | ||||
| border-top-right-radius: $teaser-border-radius; | border-top-right-radius: $teaser-border-radius; | ||||
| justify-content: center; | justify-content: center; | ||||
| align-items: center; | align-items: center; | ||||
| > img { | > img { | ||||
| padding: 5px; | |||||
| max-width: 40em; | |||||
| max-height: 20em; | |||||
| max-width: 100%; | |||||
| height: auto; | |||||
| } | } | ||||
| } | } | ||||
| > div.teaser-title { | > div.teaser-title { | ||||
| padding: 2em; | |||||
| background: transparent; | |||||
| max-width: 40em; | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| align-items: center; | |||||
| flex-direction: column; | |||||
| padding: 1em; | |||||
| text-align: center; | text-align: center; | ||||
| font-size: $header-font-size; | font-size: $header-font-size; | ||||
| } | } | ||||
| > div.date { | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| align-items: center; | |||||
| padding-bottom: 0.5rem; | |||||
| padding-top: 1rem; | |||||
| flex-direction: column; | |||||
| text-align: center; | |||||
| } | |||||
| > div.teaser-title::after { | > div.teaser-title::after { | ||||
| content: ""; | content: ""; | ||||
| background-color: $detail-color; | background-color: $detail-color; | ||||
| .teaser { | .teaser { | ||||
| cursor: pointer; | cursor: pointer; | ||||
| border: 1px solid $border-color; | border: 1px solid $border-color; | ||||
| width: 45em; | |||||
| min-height: 20em; | |||||
| max-width: 30em; | |||||
| min-height: max-content; | |||||
| border-radius: $teaser-border-radius; | border-radius: $teaser-border-radius; | ||||
| } | } | ||||
| animation: hover 0.3s cubic-bezier(0.39, 0.575, 0.565, 1) both; | animation: hover 0.3s cubic-bezier(0.39, 0.575, 0.565, 1) both; | ||||
| } | } | ||||
| .teaser-content { | .teaser-content { | ||||
| background: $teaser-background; | background: $teaser-background; | ||||
| border-bottom-left-radius: $teaser-border-radius; | border-bottom-left-radius: $teaser-border-radius; | ||||
| border-bottom-right-radius: $teaser-border-radius; | border-bottom-right-radius: $teaser-border-radius; | ||||
| min-width: 10em; | |||||
| padding: 5px; | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| align-items: center; | |||||
| flex-direction: column; | |||||
| padding: 1em; | |||||
| } | } | ||||
| .teaser-footer { | .teaser-footer { | ||||
| color: $detail-color; | color: $detail-color; | ||||
| text-decoration: none; | text-decoration: none; | ||||
| } | } | ||||
| @media (max-width: 600px) { | |||||
| div.teaser-title { | |||||
| font-size: $header-font-size / 2 !important; | |||||
| } | |||||
| } |
| } | } | ||||
| .title { | .title { | ||||
| padding-top: 1em; | |||||
| background: transparent; | background: transparent; | ||||
| font-size: $header-font-size; | |||||
| } | } | ||||
| .flag { | .flag { | ||||
| width: 160em; | width: 160em; | ||||
| height: 10em; | height: 10em; | ||||
| } | } | ||||
| @media (max-width: 600px) { | |||||
| .filter-bar { | |||||
| flex-flow: column; | |||||
| justify-content: center; | |||||
| } | |||||
| } |
| }) | }) | ||||
| export class TopNewsDashboardComponent implements OnInit { | export class TopNewsDashboardComponent implements OnInit { | ||||
| news: Article[]; | news: Article[]; | ||||
| searchDto: TopHeadlineSearchDto = { apiKey }; | |||||
| searchDto: TopHeadlineSearchDto = { apiKey, }; | |||||
| selectedCountry: { key: string, value: CountryCode }; | selectedCountry: { key: string, value: CountryCode }; | ||||
| selectedCategory: { key: string, value: Category }; | selectedCategory: { key: string, value: Category }; | ||||
| CountryCodes = CountryCode; | CountryCodes = CountryCode; |
| height: 100%; | height: 100%; | ||||
| margin: 0px; | margin: 0px; | ||||
| } | } | ||||
| .title { | |||||
| font-size: $header-font-size; | |||||
| } | |||||
| @media (max-width: 600px) { | |||||
| .title { | |||||
| font-size: $header-font-size / 2; | |||||
| } | |||||
| } | |||||
| $base-color: transparent; | $base-color: transparent; | ||||
| $primary-color: #2f85f6; | $primary-color: #2f85f6; | ||||
| $secondary-color: #8c3cee; | $secondary-color: #8c3cee; | ||||
| $border-color: #d8d8d8; | $border-color: #d8d8d8; | ||||
| $footer-height: 1.2em; | $footer-height: 1.2em; | ||||
| $default-font-size: 15px; | $default-font-size: 15px; | ||||
| $header-font-size: 25px; | $header-font-size: 25px; |