feat(aio): implement survey notification link (#21371)
Closes #21094 PR Close #21371
This commit is contained in:
parent
6af3672185
commit
4f3149242f
|
@ -3,17 +3,17 @@
|
||||||
"master": {
|
"master": {
|
||||||
"gzip7": {
|
"gzip7": {
|
||||||
"inline": 961,
|
"inline": 961,
|
||||||
"main": 115381,
|
"main": 116924,
|
||||||
"polyfills": 12962
|
"polyfills": 12962
|
||||||
},
|
},
|
||||||
"gzip9": {
|
"gzip9": {
|
||||||
"inline": 961,
|
"inline": 961,
|
||||||
"main": 115186,
|
"main": 116712,
|
||||||
"polyfills": 12958
|
"polyfills": 12958
|
||||||
},
|
},
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"inline": 1602,
|
"inline": 1602,
|
||||||
"main": 453905,
|
"main": 459119,
|
||||||
"polyfills": 40264
|
"polyfills": 40264
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,29 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-toolbar color="primary" class="app-toolbar" [class.transitioning]="isTransitioning">
|
<mat-toolbar color="primary" class="app-toolbar" [class.transitioning]="isTransitioning">
|
||||||
<button mat-button class="hamburger" (click)="sidenav.toggle()" title="Docs menu">
|
<mat-toolbar-row class="notification-container">
|
||||||
<mat-icon svgIcon="menu"></mat-icon>
|
<aio-notification
|
||||||
</button>
|
icon="insert_comment"
|
||||||
<a class="nav-link home" href="/" [ngSwitch]="isSideBySide">
|
iconLabel="Survey"
|
||||||
<img *ngSwitchCase="true" src="assets/images/logos/angular/logo-nav@2x.png" width="150" height="40" title="Home" alt="Home">
|
buttonText="Go to survey"
|
||||||
<img *ngSwitchDefault src="assets/images/logos/angular/shield-large.svg" width="37" height="40" title="Home" alt="Home">
|
actionUrl="https://bit.ly/angular-survey-2018"
|
||||||
</a>
|
notificationId="survey-january-2018"
|
||||||
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
|
expirationDate="2018-01-19"
|
||||||
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
|
(dismissed)="notificationDismissed()">
|
||||||
|
Help Angular by taking a <b>1 minute survey</b>!
|
||||||
|
</aio-notification>
|
||||||
|
</mat-toolbar-row>
|
||||||
|
<mat-toolbar-row>
|
||||||
|
<button mat-button class="hamburger" (click)="sidenav.toggle()" title="Docs menu">
|
||||||
|
<mat-icon svgIcon="menu"></mat-icon>
|
||||||
|
</button>
|
||||||
|
<a class="nav-link home" href="/" [ngSwitch]="isSideBySide">
|
||||||
|
<img *ngSwitchCase="true" src="assets/images/logos/angular/logo-nav@2x.png" width="150" height="40" title="Home" alt="Home">
|
||||||
|
<img *ngSwitchDefault src="assets/images/logos/angular/shield-large.svg" width="37" height="40" title="Home" alt="Home">
|
||||||
|
</a>
|
||||||
|
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
|
||||||
|
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
|
||||||
|
</mat-toolbar-row>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
<aio-search-results #searchResultsView *ngIf="showSearchResults" [searchResults]="searchResults | async" (resultSelected)="hideSearchResults()"></aio-search-results>
|
<aio-search-results #searchResultsView *ngIf="showSearchResults" [searchResults]="searchResults | async" (resultSelected)="hideSearchResults()"></aio-search-results>
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { CurrentNodes, NavigationService, NavigationNode, VersionInfo } from 'ap
|
||||||
import { DocumentService, DocumentContents } from 'app/documents/document.service';
|
import { DocumentService, DocumentContents } from 'app/documents/document.service';
|
||||||
import { Deployment } from 'app/shared/deployment.service';
|
import { Deployment } from 'app/shared/deployment.service';
|
||||||
import { LocationService } from 'app/shared/location.service';
|
import { LocationService } from 'app/shared/location.service';
|
||||||
|
import { NotificationComponent } from 'app/layout/notification/notification.component';
|
||||||
import { ScrollService } from 'app/shared/scroll.service';
|
import { ScrollService } from 'app/shared/scroll.service';
|
||||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||||
import { SearchResults } from 'app/search/interfaces';
|
import { SearchResults } from 'app/search/interfaces';
|
||||||
|
@ -90,6 +91,10 @@ export class AppComponent implements OnInit {
|
||||||
@ViewChild(MatSidenav)
|
@ViewChild(MatSidenav)
|
||||||
sidenav: MatSidenav;
|
sidenav: MatSidenav;
|
||||||
|
|
||||||
|
@ViewChild(NotificationComponent)
|
||||||
|
notification: NotificationComponent;
|
||||||
|
notificationAnimating = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public deployment: Deployment,
|
public deployment: Deployment,
|
||||||
private documentService: DocumentService,
|
private documentService: DocumentService,
|
||||||
|
@ -273,14 +278,33 @@ export class AppComponent implements OnInit {
|
||||||
this.folderId = (id === 'index') ? 'home' : id.split('/', 1)[0];
|
this.folderId = (id === 'index') ? 'home' : id.split('/', 1)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notificationDismissed() {
|
||||||
|
this.notificationAnimating = true;
|
||||||
|
// this should be kept in sync with the animation durations in:
|
||||||
|
// - aio/src/styles/2-modules/_notification.scss
|
||||||
|
// - aio/src/app/layout/notification/notification.component.ts
|
||||||
|
setTimeout(() => this.notificationAnimating = false, 250);
|
||||||
|
this.updateHostClasses();
|
||||||
|
}
|
||||||
|
|
||||||
updateHostClasses() {
|
updateHostClasses() {
|
||||||
const mode = `mode-${this.deployment.mode}`;
|
const mode = `mode-${this.deployment.mode}`;
|
||||||
const sideNavOpen = `sidenav-${this.sidenav.opened ? 'open' : 'closed'}`;
|
const sideNavOpen = `sidenav-${this.sidenav.opened ? 'open' : 'closed'}`;
|
||||||
const pageClass = `page-${this.pageId}`;
|
const pageClass = `page-${this.pageId}`;
|
||||||
const folderClass = `folder-${this.folderId}`;
|
const folderClass = `folder-${this.folderId}`;
|
||||||
const viewClasses = Object.keys(this.currentNodes || {}).map(view => `view-${view}`).join(' ');
|
const viewClasses = Object.keys(this.currentNodes || {}).map(view => `view-${view}`).join(' ');
|
||||||
|
const notificationClass = `aio-notification-${this.notification.showNotification}`;
|
||||||
|
const notificationAnimatingClass = this.notificationAnimating ? 'aio-notification-animating' : '';
|
||||||
|
|
||||||
this.hostClasses = `${mode} ${sideNavOpen} ${pageClass} ${folderClass} ${viewClasses}`;
|
this.hostClasses = [
|
||||||
|
mode,
|
||||||
|
sideNavOpen,
|
||||||
|
pageClass,
|
||||||
|
folderClass,
|
||||||
|
viewClasses,
|
||||||
|
notificationClass,
|
||||||
|
notificationAnimatingClass
|
||||||
|
].join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHostClassesForDoc(doc: DocumentContents) {
|
updateHostClassesForDoc(doc: DocumentContents) {
|
||||||
|
|
|
@ -34,8 +34,10 @@ import { NavItemComponent } from 'app/layout/nav-item/nav-item.component';
|
||||||
import { ScrollService } from 'app/shared/scroll.service';
|
import { ScrollService } from 'app/shared/scroll.service';
|
||||||
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
|
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
|
||||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||||
|
import { NotificationComponent } from 'app/layout/notification/notification.component';
|
||||||
import { TocComponent } from 'app/layout/toc/toc.component';
|
import { TocComponent } from 'app/layout/toc/toc.component';
|
||||||
import { TocService } from 'app/shared/toc.service';
|
import { TocService } from 'app/shared/toc.service';
|
||||||
|
import { CurrentDateToken, currentDateProvider } from 'app/shared/current-date';
|
||||||
import { WindowToken, windowProvider } from 'app/shared/window';
|
import { WindowToken, windowProvider } from 'app/shared/window';
|
||||||
|
|
||||||
import { EmbedComponentsModule } from 'app/embed-components/embed-components.module';
|
import { EmbedComponentsModule } from 'app/embed-components/embed-components.module';
|
||||||
|
@ -65,6 +67,30 @@ export const svgIconProviders = [
|
||||||
'viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>'
|
'viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>'
|
||||||
},
|
},
|
||||||
multi: true
|
multi: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: SVG_ICONS,
|
||||||
|
useValue: {
|
||||||
|
name: 'insert_comment',
|
||||||
|
svgSource:
|
||||||
|
'<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">' +
|
||||||
|
'<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/>' +
|
||||||
|
'<path d="M0 0h24v24H0z" fill="none"/>' +
|
||||||
|
'</svg>'
|
||||||
|
},
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: SVG_ICONS,
|
||||||
|
useValue: {
|
||||||
|
name: 'close',
|
||||||
|
svgSource:
|
||||||
|
'<svg fill="#ffffff" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">' +
|
||||||
|
'<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>' +
|
||||||
|
'<path d="M0 0h24v24H0z" fill="none"/>' +
|
||||||
|
'</svg>'
|
||||||
|
},
|
||||||
|
multi: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -91,6 +117,7 @@ export const svgIconProviders = [
|
||||||
NavMenuComponent,
|
NavMenuComponent,
|
||||||
NavItemComponent,
|
NavItemComponent,
|
||||||
SearchBoxComponent,
|
SearchBoxComponent,
|
||||||
|
NotificationComponent,
|
||||||
TocComponent,
|
TocComponent,
|
||||||
TopMenuComponent,
|
TopMenuComponent,
|
||||||
],
|
],
|
||||||
|
@ -109,6 +136,7 @@ export const svgIconProviders = [
|
||||||
SearchService,
|
SearchService,
|
||||||
svgIconProviders,
|
svgIconProviders,
|
||||||
TocService,
|
TocService,
|
||||||
|
{ provide: CurrentDateToken, useFactory: currentDateProvider },
|
||||||
{ provide: WindowToken, useFactory: windowProvider },
|
{ provide: WindowToken, useFactory: windowProvider },
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<a href="{{actionUrl}}" class="content" (click)="dismiss()">
|
||||||
|
<mat-icon class="icon" [svgIcon]="icon" [attr.aria-label]="iconLabel"></mat-icon>
|
||||||
|
<span class="message"><ng-content></ng-content></span>
|
||||||
|
<span class="action-button">{{buttonText}}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button mat-icon-button class="close-button" aria-label="Close" (click)="dismiss()">
|
||||||
|
<mat-icon svgIcon="close" aria-label="Dismiss notification"></mat-icon>
|
||||||
|
</button>
|
|
@ -0,0 +1,123 @@
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { CurrentDateToken } from 'app/shared/current-date';
|
||||||
|
import { NotificationComponent } from './notification.component';
|
||||||
|
import { WindowToken } from 'app/shared/window';
|
||||||
|
|
||||||
|
describe('NotificationComponent', () => {
|
||||||
|
let element: HTMLElement;
|
||||||
|
let component: NotificationComponent;
|
||||||
|
let fixture: ComponentFixture<TestComponent>;
|
||||||
|
|
||||||
|
function configTestingModule(now = new Date('2018-01-20')) {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [TestComponent, NotificationComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: WindowToken, useClass: MockWindow },
|
||||||
|
{ provide: CurrentDateToken, useValue: now },
|
||||||
|
],
|
||||||
|
imports: [NoopAnimationsModule],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createComponent() {
|
||||||
|
fixture = TestBed.createComponent(TestComponent);
|
||||||
|
const debugElement = fixture.debugElement.query(By.directive(NotificationComponent));
|
||||||
|
element = debugElement.nativeElement;
|
||||||
|
component = debugElement.componentInstance;
|
||||||
|
component.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should display the message', () => {
|
||||||
|
configTestingModule();
|
||||||
|
createComponent();
|
||||||
|
expect(fixture.nativeElement.innerHTML).toContain('Help Angular by taking a <strong>1 minute survey</strong>!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an icon', () => {
|
||||||
|
configTestingModule();
|
||||||
|
createComponent();
|
||||||
|
const iconElement = fixture.debugElement.query(By.css('.icon'));
|
||||||
|
expect(iconElement.properties['svgIcon']).toEqual('insert_comment');
|
||||||
|
expect(iconElement.attributes['aria-label']).toEqual('Survey');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display a button', () => {
|
||||||
|
configTestingModule();
|
||||||
|
createComponent();
|
||||||
|
const button = fixture.debugElement.query(By.css('.action-button'));
|
||||||
|
expect(button.nativeElement.textContent).toEqual('Go to survey');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call dismiss when the message link is clicked', () => {
|
||||||
|
configTestingModule();
|
||||||
|
createComponent();
|
||||||
|
spyOn(component, 'dismiss');
|
||||||
|
fixture.debugElement.query(By.css('a')).triggerEventHandler('click', null);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.dismiss).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call dismiss when the close button is clicked', () => {
|
||||||
|
configTestingModule();
|
||||||
|
createComponent();
|
||||||
|
spyOn(component, 'dismiss');
|
||||||
|
fixture.debugElement.query(By.css('button')).triggerEventHandler('click', null);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.dismiss).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hide the notification when dismiss is called', () => {
|
||||||
|
configTestingModule();
|
||||||
|
createComponent();
|
||||||
|
expect(component.showNotification).toBe('show');
|
||||||
|
component.dismiss();
|
||||||
|
expect(component.showNotification).toBe('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update localStorage key when dismiss is called', () => {
|
||||||
|
configTestingModule();
|
||||||
|
createComponent();
|
||||||
|
const setItemSpy: jasmine.Spy = TestBed.get(WindowToken).localStorage.setItem;
|
||||||
|
component.dismiss();
|
||||||
|
expect(setItemSpy).toHaveBeenCalledWith('aio-notification/survey-january-2018', 'hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show the notification if the date is after the expiry date', () => {
|
||||||
|
configTestingModule(new Date('2018-01-23'));
|
||||||
|
createComponent();
|
||||||
|
expect(component.showNotification).toBe('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show the notification if the there is a "hide" flag in localStorage', () => {
|
||||||
|
configTestingModule();
|
||||||
|
const getItemSpy: jasmine.Spy = TestBed.get(WindowToken).localStorage.getItem;
|
||||||
|
getItemSpy.and.returnValue('hide');
|
||||||
|
createComponent();
|
||||||
|
expect(getItemSpy).toHaveBeenCalledWith('aio-notification/survey-january-2018');
|
||||||
|
expect(component.showNotification).toBe('hide');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<aio-notification
|
||||||
|
icon="insert_comment"
|
||||||
|
iconLabel="Survey"
|
||||||
|
buttonText="Go to survey"
|
||||||
|
actionUrl="https://bit.ly/angular-survey-2018"
|
||||||
|
notificationId="survey-january-2018"
|
||||||
|
expirationDate="2018-01-22">
|
||||||
|
Help Angular by taking a <strong>1 minute survey</strong>!
|
||||||
|
</aio-notification>`
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockWindow {
|
||||||
|
localStorage = jasmine.createSpyObj('localStorage', ['getItem', 'setItem']);
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { animate, state, style, trigger, transition } from '@angular/animations';
|
||||||
|
import { Component, EventEmitter, HostBinding, Inject, Input, OnInit, Output } from '@angular/core';
|
||||||
|
import { CurrentDateToken } from 'app/shared/current-date';
|
||||||
|
import { WindowToken } from 'app/shared/window';
|
||||||
|
|
||||||
|
const LOCAL_STORAGE_NAMESPACE = 'aio-notification/';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-notification',
|
||||||
|
templateUrl: 'notification.component.html',
|
||||||
|
animations: [
|
||||||
|
trigger('hideAnimation', [
|
||||||
|
state('show', style({height: '*'})),
|
||||||
|
state('hide', style({height: 0})),
|
||||||
|
// this should be kept in sync with the animation durations in:
|
||||||
|
// - aio/src/styles/2-modules/_notification.scss
|
||||||
|
// - aio/src/app/app.component.ts : notificationDismissed()
|
||||||
|
transition('show => hide', animate(250))
|
||||||
|
])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class NotificationComponent implements OnInit {
|
||||||
|
private get localStorage() { return this.window.localStorage; }
|
||||||
|
|
||||||
|
@Input() icon: string;
|
||||||
|
@Input() iconLabel: string;
|
||||||
|
@Input() buttonText: string;
|
||||||
|
@Input() actionUrl: string;
|
||||||
|
@Input() notificationId: string;
|
||||||
|
@Input() expirationDate: string;
|
||||||
|
@Output() dismissed = new EventEmitter();
|
||||||
|
|
||||||
|
@HostBinding('@hideAnimation')
|
||||||
|
showNotification: 'show'|'hide';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(WindowToken) private window: Window,
|
||||||
|
@Inject(CurrentDateToken) private currentDate: Date
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
const previouslyHidden = this.localStorage.getItem(LOCAL_STORAGE_NAMESPACE + this.notificationId) === 'hide';
|
||||||
|
const expired = this.currentDate > new Date(this.expirationDate);
|
||||||
|
this.showNotification = previouslyHidden || expired ? 'hide' : 'show';
|
||||||
|
}
|
||||||
|
|
||||||
|
dismiss() {
|
||||||
|
this.localStorage.setItem(LOCAL_STORAGE_NAMESPACE + this.notificationId, 'hide');
|
||||||
|
this.showNotification = 'hide';
|
||||||
|
this.dismissed.next();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { InjectionToken } from '@angular/core';
|
||||||
|
|
||||||
|
export const CurrentDateToken = new InjectionToken('CurrentDate');
|
||||||
|
export function currentDateProvider() { return new Date(); }
|
|
@ -10,9 +10,12 @@ mat-toolbar.mat-toolbar {
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
padding: 0 16px 0 0;
|
|
||||||
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.30);
|
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.30);
|
||||||
|
|
||||||
|
mat-toolbar-row {
|
||||||
|
padding: 0 16px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
mat-icon {
|
mat-icon {
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,3 +29,4 @@
|
||||||
@import 'toc';
|
@import 'toc';
|
||||||
@import 'select-menu';
|
@import 'select-menu';
|
||||||
@import 'deploy-theme';
|
@import 'deploy-theme';
|
||||||
|
@import 'notification';
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
$notificationHeight: 56px;
|
||||||
|
|
||||||
|
// we need to override some of the toolbar styling
|
||||||
|
.mat-toolbar mat-toolbar-row.notification-container {
|
||||||
|
padding: 0;
|
||||||
|
height: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
aio-notification {
|
||||||
|
background: $darkgray;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: $notificationHeight;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
@media (max-width: 430px) {
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: $notificationHeight;
|
||||||
|
height: $notificationHeight;
|
||||||
|
background: $darkgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
max-width: calc(100% - #{$notificationHeight});
|
||||||
|
text-transform: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
@media (max-width: 464px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
margin-left: 10px;
|
||||||
|
background: $brightred;
|
||||||
|
border-radius: 15px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
@media (max-width: 780px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here are all the hacks to make the content and sidebars the right height
|
||||||
|
// when the notification is visible
|
||||||
|
.aio-notification-show {
|
||||||
|
.sidenav-content {
|
||||||
|
padding-top: 80px + $notificationHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-sidenav.mat-sidenav.sidenav {
|
||||||
|
top: 56px + $notificationHeight;
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
top: 56px + $notificationHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-container {
|
||||||
|
top: 76px + $notificationHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results {
|
||||||
|
padding-top: 68px + $notificationHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.page-home, &.page-resources, &.page-events, &.page-features, &.page-presskit, &.page-contribute {
|
||||||
|
section {
|
||||||
|
padding-top: $notificationHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animate the content when the notification bar is dismissed
|
||||||
|
// this should be kept in sync with the animation durations in
|
||||||
|
// - aio/src/app/layout/notification/notification.component.ts
|
||||||
|
// - aio/src/app/app.component.ts : notificationDismissed()
|
||||||
|
.aio-notification-animating {
|
||||||
|
.sidenav-content {
|
||||||
|
transition: padding-top 250ms ease;
|
||||||
|
}
|
||||||
|
mat-sidenav.mat-sidenav.sidenav, .toc-container {
|
||||||
|
transition: top 250ms ease;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue