feat: docs app

This commit is contained in:
Sergey Andrievskiy 2019-07-16 08:38:11 +03:00
parent 713aff561e
commit 2129689f98
203 changed files with 15927 additions and 5 deletions

View file

@ -0,0 +1,74 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { NgxLandingThemeModule } from '../@theme/theme.module';
import {
NgxMdBLockComponent,
NgxTabbedBlockComponent,
NgxOverviewBlockComponent,
NgxExampleBlockComponent,
NgxInlineExampleBlockComponent,
NgxTabbedExampleBlockComponent,
NgxLiveExampleBlockComponent,
NgxStackedExampleComponent,
NgxCodeBlockComponent,
NgxMethodsBlockComponent,
NgxPropsBlockComponent,
NgxPropBlockComponent,
NgxStylesBlockComponent,
NgxThemeComponent,
NgxComponentBlockComponent,
NgxApiBlockComponent,
NgxStylesTableBlockComponent,
NgxExamplesBlockComponent,
NgxPagerBlockComponent,
NgxComponentsOverviewBlockComponent,
} from './components/';
const blocks = [
NgxMdBLockComponent,
NgxTabbedBlockComponent,
NgxOverviewBlockComponent,
NgxExampleBlockComponent,
NgxInlineExampleBlockComponent,
NgxTabbedExampleBlockComponent,
NgxLiveExampleBlockComponent,
NgxStackedExampleComponent,
NgxCodeBlockComponent,
NgxMethodsBlockComponent,
NgxPropsBlockComponent,
NgxPropBlockComponent,
NgxStylesBlockComponent,
NgxThemeComponent,
NgxComponentBlockComponent,
NgxApiBlockComponent,
NgxStylesTableBlockComponent,
NgxExamplesBlockComponent,
NgxPagerBlockComponent,
NgxComponentsOverviewBlockComponent,
];
@NgModule({
imports: [
CommonModule,
RouterModule,
NgxLandingThemeModule,
],
declarations: [
...blocks,
],
exports: [
CommonModule,
RouterModule,
...blocks,
],
})
export class NgxBlocksModule {
}

View file

@ -0,0 +1,38 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { NgxTabbedService } from '../../../@theme/services/tabbed.service';
@Component({
selector: 'ngx-api-block',
template: `
<nb-card [ngxFragment]="source.slag">
<nb-card-body>
<h2>{{ source.name }}</h2>
<ngx-props-block [source]="source" *ngIf="hasProps(source)"></ngx-props-block>
<ngx-methods-block [source]="source" *ngIf="hasMethods(source)"></ngx-methods-block>
</nb-card-body>
</nb-card>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxApiBlockComponent {
@Input('source') source;
constructor(private tabbedService: NgxTabbedService) {
}
hasMethods(component) {
return this.tabbedService.componentHasMethods(component);
}
hasProps(component) {
return this.tabbedService.componentHasProps(component);
}
}

View file

@ -0,0 +1,39 @@
@import '../../../@theme/styles/themes';
@include nb-install-component() {
$code-lines-fg: #515877;
$code-block-bg: nb-theme(code-block-bg);
.container {
display: flex;
padding: 0;
font-size: 1rem;
border-radius: 0.5rem;
background: $code-block-bg;
overflow-x: auto;
.lines {
display: flex;
flex-direction: column;
text-align: end;
font-size: 0.875rem;
padding: 2rem 0.5rem 0.5rem;
border-radius: 0.5rem 0 0 0.5rem;
color: $code-lines-fg;
user-select: none;
}
pre {
margin-bottom: 0;
background: transparent;
overflow: visible;
code.hljs {
background: transparent;
padding-left: 0.5rem;
margin-bottom: 0;
}
}
}
}

View file

@ -0,0 +1,48 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { SafeHtml } from '@angular/platform-browser';
import { NgxHighlightService } from '../../../@theme/services/highlight.service';
@Component({
selector: 'ngx-code-block',
styleUrls: ['./code-block.component.scss'],
template: `
<div class="container">
<div class="lines">
<span *ngFor="let line of lines">{{ line }}</span>
</div>
<pre><code class="hljs" [innerHTML]="code"></code></pre>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxCodeBlockComponent {
@Input() path = '';
@Input() firstLine: number;
@Input() lastLine: number;
@Input('code')
set rawCode(value) {
const highlighted = this.highlightService.highlight(value);
this.code = this.getVisible(highlighted);
this.lines = this.createLines(this.code);
}
code: SafeHtml;
lines: number[] = [];
constructor(private highlightService: NgxHighlightService) {
}
getVisible(code): string {
return code
.split('\n')
.slice(this.firstLine - 1, this.lastLine)
.join('\n');
}
createLines(code): number[] {
const length = code.split('\n').length;
return Array(length).fill(0).map((_, i) => i + (this.firstLine || 1));
}
}

View file

@ -0,0 +1,59 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { NgxTabbedService } from '../../../@theme/services/tabbed.service';
@Component({
selector: 'ngx-component-block',
template: `
<nb-card [ngxFragment]="source.slag">
<nb-card-body>
<ng-container class="description" *ngFor="let node of overview">
<ng-container *ngIf="node.type === 'text'">
<div *ngFor="let section of node.content" [innerHtml]="section.html"></div>
</ng-container>
<ngx-live-example-block *ngIf="node.type === 'live-example'" [id]="node.content" [title]="'example'"
class="widget-block">
</ngx-live-example-block>
<ngx-inline-example-block *ngIf="node.type === 'inline-example'" [content]="node.content"
class="widget-block">
</ngx-inline-example-block>
<ngx-stacked-example-block *ngIf="node.type === 'example'" [content]="node.content"
class="widget-block">
</ngx-stacked-example-block>
</ng-container>
<ngx-props-block [source]="source" *ngIf="hasProps(source)"></ngx-props-block>
<ngx-methods-block [source]="source" *ngIf="hasMethods(source)"></ngx-methods-block>
<ng-container *ngIf="hasTheme(source)">
<h3>Theme</h3>
<ngx-styles-table-block [source]="source"></ngx-styles-table-block>
</ng-container>
</nb-card-body>
</nb-card>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxComponentBlockComponent {
source: any;
overview: any[] = [];
@Input('source')
set setSource(source: any) {
this.source = source;
this.overview = source.overview;
}
constructor(private tabbedService: NgxTabbedService) {
}
hasTheme(component) {
return this.tabbedService.componentHasTheme(component);
}
hasMethods(component) {
return this.tabbedService.componentHasMethods(component);
}
hasProps(component) {
return this.tabbedService.componentHasProps(component);
}
}

View file

@ -0,0 +1,20 @@
<nb-card class="header-card">
<nb-card-header>Components Overview</nb-card-header>
</nb-card>
<div class="components-list">
<ng-container *ngFor="let component of components">
<h2 *ngIf="component.group">{{ component.name }}</h2>
<div *ngIf="!component.group" class="component-card-wrapper">
<a class="component-navigate-link" [routerLink]="component.link">
<nb-card [attr.title]="component.name">
<nb-card-body>
<img class="component-icon" src="assets/images/components/{{ component.icon }}"
[attr.alt]="component.name">
<label class="component-name">{{ component.name }}</label>
</nb-card-body>
</nb-card>
</a>
</div>
</ng-container>
</div>

View file

@ -0,0 +1,81 @@
@import '../../../@theme/styles/themes';
@import '~@nebular/theme/styles/global/breakpoints';
@include nb-install-component() {
.components-list {
display: flex;
flex-wrap: wrap;
h2 {
flex: 1 1 100%;
color: nb-theme(color-fg-heading-light);
margin: 1rem 0 2rem;
text-align: center;
}
.component-card-wrapper {
width: 100%;
}
.component-icon {
margin-bottom: 1rem;
}
.component-name {
color: nb-theme(color-fg-heading-light);
font-weight: nb-theme(font-weight-bolder);
}
.component-navigate-link {
text-decoration: none;
}
nb-card {
box-shadow: 0 4px 27px 0 rgba(230, 234, 240, 0.2);
transition: transform 0.25s ease;
> nb-card-body {
height: 12.5rem;
padding: 2rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
&:hover {
box-shadow: 0 15px 37px 0 #dbe2eb;
transform: translateY(-1rem);
.component-name {
color: nb-theme(color-fg);
}
}
}
}
@include media-breakpoint-up(is) {
.components-list {
.component-card-wrapper {
flex: 1 0 auto;
width: 50%;
padding-left: 1rem;
padding-right: 1rem;
}
}
}
@include media-breakpoint-up(md) {
.components-list {
.component-card-wrapper {
flex: 1 0 auto;
max-width: 33.3%;
padding-left: 1.5rem;
padding-right: 1.5rem;
}
}
}
}

View file

@ -0,0 +1,23 @@
import { Component, OnInit } from '@angular/core';
import { NgxMenuService } from '../../../@theme/services/menu.service';
@Component({
selector: 'ngx-components-overview-block',
styleUrls: ['./components-overview-block.component.scss'],
templateUrl: './components-overview-block.component.html',
})
export class NgxComponentsOverviewBlockComponent implements OnInit {
components: { name: string; icon: string; link: string }[];
constructor(private menu: NgxMenuService) {}
ngOnInit() {
this.components = this.menu
.getPreparedMenu('/docs')
.find(({ title }) => title === 'Components')
.children
.slice(1)
.map(({ data: { name, icon, type }, link }) => ({ name, icon, link, group: type === 'group' }));
}
}

View file

@ -0,0 +1,43 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Input,
} from '@angular/core';
import { NgxCodeLoaderService } from '../../../@theme/services/code-loader.service';
@Component({
selector: 'ngx-example-block',
template: `
<ngx-code-block *ngIf="code"
[firstLine]="firstLine"
[lastLine]="lastLine"
[code]="code">
</ngx-code-block>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxExampleBlockComponent {
code: string;
firstLine: number;
lastLine: number;
@Input('content')
set setContent(content) {
this.loadCode(content);
}
constructor(private codeLoader: NgxCodeLoaderService, private cd: ChangeDetectorRef) {
}
loadCode(content) {
this.codeLoader.load(content.files[0])
.subscribe((code: string) => {
this.code = code;
this.firstLine = content.firstLine || 1;
this.lastLine = content.lastLine || code.split('\n').length;
this.cd.detectChanges();
});
}
}

View file

@ -0,0 +1,27 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'ngx-examples-block',
template: `
<nb-card [ngxFragment]="source.slag">
<nb-card-body>
<h2>{{ source.name }}</h2>
<ngx-stacked-example-block *ngFor="let example of source.liveExamples" [content]="example.content"
class="widget-block">
</ngx-stacked-example-block>
</nb-card-body>
</nb-card>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxExamplesBlockComponent {
@Input('source') source;
}

View file

@ -0,0 +1,20 @@
export * from './md-block/md-block.component';
export * from './tabbed-block/tabbed-block.component';
export * from './overview-block/overview-block.component';
export * from './code-block/code-block.component';
export * from './tabbed-example-block/tabbed-example-block.component';
export * from './example-block/example-block.component';
export * from './inline-example-block/inline-example-block.component';
export * from './live-example-block/live-example-block.component';
export * from './stacked-example-block/stacked-examples.component';
export * from './methods-block/methods-block.component';
export * from './props-block/props-block.component';
export * from './prop-block/prop-block.component';
export * from './styles-block/styles-block.component';
export * from './theme-block/theme-block.component';
export * from './component-block/component-block.component';
export * from './api-block/api-block.component';
export * from './styles-table-block/styles-table-block.component';
export * from './examples-block/examples-block.component';
export * from './pager-block/pager-block.component';
export * from './components-overview-block/components-overview-block.component';

View file

@ -0,0 +1,22 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'ngx-inline-example-block',
template: `
<ngx-example-block *ngIf="isOneFile" [content]="content"></ngx-example-block>
<ngx-tabbed-example-block *ngIf="isTabbed" [content]="content"></ngx-tabbed-example-block>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxInlineExampleBlockComponent {
@Input() content;
get isOneFile(): boolean {
return !this.isTabbed;
}
get isTabbed(): boolean {
return this.content.files.length > 1;
}
}

View file

@ -0,0 +1,25 @@
<section class="header" >
<strong class="title">{{ content.name }}</strong>
<div class="actions">
<div class="action-selector">
<select class="action-item" [(ngModel)]="currentTheme" (change)="switchTheme($event.target.value)">
<option *ngFor="let theme of themes" [value]="theme.value">{{theme.label}}</option>
</select>
<i class="icon feather-aperture"></i>
</div>
<a class="btn action-item action-button" target="_blank" [href]="url">
<i class="icon feather-external-link"></i>
</a>
<button type="button"
*ngIf="hasViewSwitch"
class="btn action-item action-button"
(click)="switchToInlineVew()">
<i class="icon feather-code"></i>
</button>
</div>
</section>
<div class="iframe-container">
<iframe #iframe *ngIf="content.id" [style.height.px]="iframeHeight" [class.loading]="loading"></iframe>
</div>
<span class="icon-loading feather-more-vertical" *ngIf="loading"></span>

View file

@ -0,0 +1,170 @@
@import '../../../@theme/styles/themes';
@include nb-install-component() {
// TODO: move some variables in Nebular themes
// colors
$action-bg: white;
$action-fg: nb-theme(color-fg-heading-light);
$block-bg-default: #ebeff5;
$block-bg-cosmic: #2f296b;
$block-fg-cosmic: #7d838b;
$block-bg-corporate: #f1f5f8;
display: flex;
flex-direction: column;
padding: 0.5rem 1rem 2.5rem 1.5rem;
border-radius: 0.5rem;
position: relative;
.header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
margin-bottom: 1.875rem;
}
.title,
.actions {
margin-top: 0.5rem;
}
.title {
margin-right: 1rem;
font-weight: bold;
text-transform: capitalize;
}
.actions {
display: flex;
width: 100%;
.icon {
font-size: 0.95rem;
}
}
.action-item {
background-color: $action-bg;
border-radius: 0.375rem;
height: 100%;
line-height: 1;
border: none;
color: $action-fg;
padding: 0.5rem 1rem;
margin-left: 0.625rem;
cursor: pointer;
&:first-child {
margin-left: 0;
}
&:hover, &:focus {
text-decoration: none;
}
}
.action-selector {
position: relative;
.action-item {
padding: 0;
color: transparent;
}
.icon {
color: $action-fg;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* Target IE9 - IE11 */
select::-ms-expand {
display: none;
}
select {
font-size: 0.875rem;
appearance: none;
}
}
&.theme-default {
background-color: $block-bg-default;
}
&.theme-cosmic {
background-color: $block-bg-cosmic;
.title {
color: white;
}
.action-item {
color: $block-fg-cosmic;
}
}
&.theme-corporate {
background-color: $block-bg-corporate;
}
.iframe-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
iframe {
width: 100%;
border: none;
transform: translateZ(0);
&.loading {
visibility: hidden;
}
}
.icon-loading {
animation: rotation 2s infinite linear;
color: $action-fg;
font-size: 1.5rem;
font-weight: normal;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
}
}
@media screen and (min-width: 23em) {
.action-selector {
.action-item {
padding: 0.5rem 1rem;
color: $action-fg;
}
select.action-item {
padding: 0 2.5rem;
}
.icon {
left: 1.25rem;
transform: translate(0, -50%);
}
}
.actions {
width: auto;
}
}
}

View file

@ -0,0 +1,105 @@
import {
Component,
ElementRef,
Input,
OnInit,
OnDestroy,
ViewChild,
ChangeDetectionStrategy,
ChangeDetectorRef,
HostBinding,
Output,
EventEmitter,
AfterViewInit,
} from '@angular/core';
import { Location } from '@angular/common';
import { takeWhile } from 'rxjs/operators';
import { NgxExampleView } from '../../enum.example-view';
import { NgxIframeCommunicatorService } from '../../../@theme/services/iframe-communicator.service';
@Component({
selector: 'ngx-live-example-block',
styleUrls: ['./live-example-block.component.scss'],
templateUrl: './live-example-block.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxLiveExampleBlockComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('iframe', { static: false }) iframe: ElementRef;
@Input() content: any;
@Input() hasViewSwitch: boolean = false;
@Output() changeView = new EventEmitter<NgxExampleView>();
/* tslint:disable:no-unused-variable */
@HostBinding('class.theme-default')
private get isDefault() {
return this.currentTheme === 'default';
}
@HostBinding('class.theme-cosmic')
private get isCosmic() {
return this.currentTheme === 'cosmic';
}
@HostBinding('class.theme-corporate')
private get isCorporate() {
return this.currentTheme === 'corporate';
}
/* tslint:enable:no-unused-variable */
iframeHeight = 0;
alive: boolean = true;
themes: {label: string; value: string}[] = [
{ label: 'Default', value: 'default' },
{ label: 'Cosmic', value: 'cosmic' },
{ label: 'Corporate', value: 'corporate' },
];
currentTheme: string = 'default';
loading = true;
get url(): string {
return this.location.prepareExternalUrl(`example/${this.content.id}`);
}
get iframeWindow(): Window {
return this.iframe.nativeElement.contentWindow;
}
constructor(private changeDetection: ChangeDetectorRef,
private location: Location,
private communicator: NgxIframeCommunicatorService) {
}
ngOnInit() {
this.communicator.receive(this.content.id)
.pipe(takeWhile(() => this.alive))
.subscribe(it => {
this.iframeHeight = it.height;
this.loading = false;
this.changeDetection.detectChanges();
});
}
ngAfterViewInit() {
// we cannot set src using angular binding
// as it will trigger change detection and reload iframe
// which in its turn will send a new height
// and we would need to set the height and trigger change detection again
// resulting in infinite loop
this.iframe.nativeElement.src = this.url;
}
ngOnDestroy() {
this.alive = false;
}
switchTheme(theme: string) {
this.communicator.send({ id: this.content.id, theme }, this.iframeWindow);
}
switchToInlineVew() {
this.changeView.emit(NgxExampleView.INLINE);
}
}

View file

@ -0,0 +1,23 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'ngx-md-block',
template: `
<nb-card *ngFor="let section of source;" [ngxFragment]="section.fragment">
<nb-card-body>
<div [innerHtml]="section.html"></div>
</nb-card-body>
</nb-card>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxMdBLockComponent {
@Input() source: string;
}

View file

@ -0,0 +1,54 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'ngx-methods-block',
template: `
<h3>Methods</h3>
<table>
<thead>
<tr>
<td width="25%">Name</td>
<td>Description</td>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let method of methods">
<tr *ngIf="method.shortDescription || method.description">
<td>{{ method.name }}() <br><i *ngIf="method.isStatic">static method</i></td>
<td>
<div class="method-signature">
<div *ngIf="method.params.length > 0">
<i>parameters:</i>
<span *ngFor="let param of method.params; let last = last">
{{ param.name }}: <code>{{ param.type }}</code><span *ngIf="!last">,</span>
</span>
</div>
<i>returns:</i>
<code>{{ method.type.join(",\\n") }}</code>
</div>
<div *ngIf="method.shortDescription || method.description" class="method-description" ngxDescription>
{{ method.shortDescription }} <br> {{ method.description }}
</div>
</td>
</tr>
</ng-container>
</tbody>
</table>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxMethodsBlockComponent {
methods: any;
@Input('source')
set setSource(source: any) {
this.methods = source.methods;
}
}

View file

@ -0,0 +1,37 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'ngx-overview-block',
template: `
<nb-card [ngxFragment]="source.slag">
<nb-card-body>
<ng-container class="description" *ngFor="let node of overview">
<ng-container *ngIf="node.type === 'text'">
<div *ngFor="let section of node.content" [innerHtml]="section.html"></div>
</ng-container>
<ngx-live-example-block *ngIf="node.type === 'live-example'" [content]="node.content"
class="widget-block">
</ngx-live-example-block>
<ngx-inline-example-block *ngIf="node.type === 'inline-example'" [content]="node.content"
class="widget-block">
</ngx-inline-example-block>
<ngx-stacked-example-block *ngIf="node.type === 'stacked-example'" [content]="node.content"
class="widget-block">
</ngx-stacked-example-block>
</ng-container>
</nb-card-body>
</nb-card>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxOverviewBlockComponent {
source: any;
overview: any[] = [];
@Input('source')
set setSource(source: any) {
this.source = source;
this.overview = source.overview;
}
}

View file

@ -0,0 +1,85 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
@import '../../../@theme/styles/themes';
@import '~@nebular/theme/styles/global/breakpoints';
@include nb-install-component() {
$title-fg: nb-theme(color-fg-heading);
$text-fg: nb-theme(color-fg-text);
$arrow-fg: nb-theme(color-fg-highlight);
display: flex;
flex-direction: column;
::ng-deep nb-card {
font-weight: 300;
flex: 1;
&.invisible {
visibility: hidden;
}
a {
padding: 2rem;
text-decoration: none;
color: $text-fg;
height: 100%;
}
.page-title {
display: flex;
justify-content: space-between;
color: $title-fg;
font-weight: 500;
font-size: 1.2rem;
i {
color: $arrow-fg;
margin-top: 0.3rem;
font-weight: bold;
font-size: 1.7rem;
}
span {
word-wrap: normal;
}
}
.description {
display: none;
}
&.left-block {
text-align: right;
}
}
@include media-breakpoint-up(sm) {
flex-direction: row;
flex-wrap: wrap;
::ng-deep nb-card {
margin-left: 1rem;
&:first-child {
margin-left: 0;
}
a {
padding: 2rem 3rem 2rem 2rem;
}
.page-title {
font-size: 1.5rem;
margin-bottom: 0.6rem;
}
.description {
display: block;
}
}
}
}

View file

@ -0,0 +1,48 @@
import {Component, ChangeDetectionStrategy, Input} from '@angular/core';
import { NgxPaginationService } from '../../../@theme/services/pagination.service';
@Component({
selector: 'ngx-pager-block',
styleUrls: ['./pager-block.component.scss'],
template: `
<ng-container *ngIf="paginationItem">
<nb-card [class.invisible]="!paginationItem.prev" class="left-block">
<a *ngIf="paginationItem.prev" [routerLink]="paginationItem.prev.link"
[attr.title]="paginationItem.prev.title">
<div class="page-title">
<i class="icon nb-arrow-thin-left"></i>
<span>{{ paginationItem.prev.title }}</span>
</div>
<div class="description">Previous page</div>
</a>
</nb-card>
<nb-card [class.invisible]="!paginationItem.next" class="right-block">
<a *ngIf="paginationItem.next" [routerLink]="paginationItem.next.link"
[attr.title]="paginationItem.next.title">
<div class="page-title">
<span>{{ paginationItem.next.title }}</span>
<i class="icon nb-arrow-thin-right"></i>
</div>
<div class="description">Next page</div>
</a>
</nb-card>
</ng-container>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxPagerBlockComponent {
paginationItem;
@Input('currentItemSlag')
set setPaginationItem(currentItemSlag: string) {
this.paginationItem = this.getPaginationItem(currentItemSlag);
}
constructor(private paginationService: NgxPaginationService) {
}
getPaginationItem(currentItemSlag) {
return this.paginationService.getPaginationItem(currentItemSlag);
}
}

View file

@ -0,0 +1,41 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'ngx-prop-block',
template: `
<h3>{{ name }}</h3>
<table>
<thead>
<tr>
<td width="25%">Name</td>
<td width="20%">Type</td>
<td>Description</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let prop of properties">
<td>{{ prop.name }}</td>
<td><code *ngIf="prop.type">{{ prop.type }}</code></td>
<td>
<div *ngIf="prop.shortDescription" ngxDescription>{{ prop.shortDescription }}</div>
<div *ngIf="prop.description" ngxDescription>{{ prop.description }}</div>
</td>
</tr>
</tbody>
</table>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxPropBlockComponent {
@Input() properties = [];
@Input() name;
@Input() slag;
}

View file

@ -0,0 +1,51 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'ngx-props-block',
template: `
<ngx-prop-block *ngIf="inputs.length > 0"
[properties]="inputs"
name="Inputs"
[slag]="slag"
class="widget-block">
</ngx-prop-block>
<ngx-prop-block *ngIf="outputs.length > 0"
[properties]="outputs"
name="Outputs"
[slag]="slag"
class="widget-block">
</ngx-prop-block>
<ngx-prop-block *ngIf="props.length > 0"
[properties]="props"
name="Properties"
[slag]="slag"
class="widget-block">
</ngx-prop-block>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxPropsBlockComponent {
outputs: any = [];
inputs: any = [];
props: any = [];
name: string;
slag: string;
@Input('source')
set setSource(source: any) {
this.inputs = source.props.filter(item => item.kind === 'input');
this.outputs = source.props.filter(item => item.kind === 'output');
this.props = source.props.filter(item => item.kind === 'property');
this.name = source.name;
this.slag = source.slag;
}
}

View file

@ -0,0 +1,59 @@
import { Component, Input } from '@angular/core';
import { NgxExampleView } from '../../enum.example-view';
import { animate, animation, keyframes, style, transition, trigger, useAnimation } from '@angular/animations';
export const pulse = animation(
animate(
'{{ timing }}s {{ delay }}s',
keyframes([
style({ transform: 'scale3d(1, 1, 1)' }),
style({ transform: 'scale3d({{ scale }}, {{ scale }}, {{ scale }})' }),
style({ transform: 'scale3d(1, 1, 1)' }),
]),
),
{ params: { scale: 1.02, timing: 0.5, delay: 0 } },
);
@Component({
selector: 'ngx-stacked-example-block',
template: `
<div>
<ngx-live-example-block [hidden]="!isLive"
[@exampleState]="isLive ? 'live': 'code'"
[content]="content"
hasViewSwitch="true"
(changeView)="changeView($event)">
</ngx-live-example-block>
<ngx-tabbed-example-block [hidden]="isLive"
[@exampleState]="isLive ? 'live': 'code'"
[content]="content"
hasViewSwitch="true"
(changeView)="changeView($event)">
</ngx-tabbed-example-block>
</div>
`,
animations: [
trigger('exampleState', [
transition('live => code', [
useAnimation(pulse),
]),
transition('code => live', [
useAnimation(pulse),
]),
]),
],
})
export class NgxStackedExampleComponent {
@Input() content: any;
isLive = true;
constructor() {
}
changeView(view: NgxExampleView) {
this.isLive = view === NgxExampleView.LIVE;
}
}

View file

@ -0,0 +1,24 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'ngx-styles-block',
template: `
<nb-card [ngxFragment]="source.slag">
<nb-card-body>
<h2>{{ source.name }}</h2>
<ngx-styles-table-block [source]="source"></ngx-styles-table-block>
</nb-card-body>
</nb-card>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxStylesBlockComponent {
@Input() source;
}

View file

@ -0,0 +1,47 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { NgxStylesService } from '../../../@theme/services/styles.service';
@Component({
selector: 'ngx-styles-table-block',
template: `
<table class="striped" *ngFor="let style of classStyles">
<thead>
<tr>
<td>Name</td>
<td *ngFor="let themedValue of style.styles[0].themedValues">{{ themedValue.theme }}</td>
<td>Description</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of style.styles">
<td>{{ item.name }}</td>
<td *ngFor="let themedValue of item.themedValues" ngxColorSwatch>{{ themedValue.value }}</td>
<td>
<p *ngIf="item.shortDescription" ngxDescription>{{ item.shortDescription}}</p>
<p *ngIf="item.description" ngxDescription>{{ item.description }}</p>
</td>
</tr>
</tbody>
</table>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxStylesTableBlockComponent {
classStyles: any;
@Input('source')
set setSource(source: any) {
this.classStyles = this.stylesService.mapThemedValues(source.styles);
}
constructor(private stylesService: NgxStylesService) {
}
}

View file

@ -0,0 +1,21 @@
<ng-container [ngSwitch]="currentTab?.tab">
<ng-container *ngFor="let component of source">
<ng-container *ngSwitchCase="'overview'">
<ngx-overview-block *ngIf="hasOverview(component)" [source]="component"></ngx-overview-block>
</ng-container>
<ng-container *ngSwitchCase="'theme'">
<ngx-styles-block *ngIf="hasTheme(component)" [source]="component"></ngx-styles-block>
</ng-container>
<ng-container *ngSwitchCase="'api'">
<ngx-api-block *ngIf="hasAPI(component)" [source]="component" ></ngx-api-block>
</ng-container>
<ng-container *ngSwitchCase="'examples'">
<ngx-examples-block *ngIf="hasExamples(component)" [source]="component" ></ngx-examples-block>
</ng-container>
</ng-container>
</ng-container>

View file

@ -0,0 +1,96 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { BehaviorSubject, combineLatest } from 'rxjs';
import { filter, takeWhile } from 'rxjs/operators';
import { NgxTabbedService } from '../../../@theme/services/tabbed.service';
@Component({
selector: 'ngx-tabbed-block',
templateUrl: './tabbed-block.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxTabbedBlockComponent implements OnDestroy {
currentTab;
@Input() source;
@Input()
set tabs(value) {
if (value) {
value = Object
.entries(value)
.filter(([key, val]) => val)
.map(([key, val]) => ({ tab: key }));
this.tabs$.next(value);
}
}
private tabs$ = new BehaviorSubject(null);
private alive = true;
constructor(private activatedRoute: ActivatedRoute,
private router: Router,
private cd: ChangeDetectorRef,
private titleService: Title,
private tabbedService: NgxTabbedService) {
combineLatest([
this.activatedRoute.params.pipe(filter((params) => !params.tab)),
this.tabs$.pipe(filter((tabs) => tabs && tabs.length)),
])
.pipe(takeWhile(() => this.alive))
.subscribe(([params, tabs]) => {
this.router.navigate([tabs[0].tab], { relativeTo: activatedRoute, replaceUrl: true });
});
combineLatest([
this.activatedRoute.params.pipe(filter((params) => params.tab)),
this.tabs$.pipe(filter((tabs) => tabs && tabs.length)),
])
.pipe(takeWhile(() => this.alive))
.subscribe(([params, tabs]) => {
this.currentTab = tabs.find(tab => tab.tab === params.tab);
if (this.currentTab) {
this.titleService.setTitle(`${this.titleService.getTitle()} - component ${this.currentTab.tab}`);
}
this.cd.detectChanges();
});
}
hasOverview(component) {
return this.tabbedService.componentHasOverview(component);
}
hasExamples(component) {
return this.tabbedService.componentHasExamples(component);
}
hasTheme(component) {
return this.tabbedService.componentHasTheme(component);
}
hasMethods(component) {
return this.tabbedService.componentHasMethods(component);
}
hasProps(component) {
return this.tabbedService.componentHasProps(component);
}
hasAPI(component) {
return this.hasMethods(component) || this.hasProps(component);
}
ngOnDestroy() {
this.alive = false;
}
}

View file

@ -0,0 +1,13 @@
<button type="button"
*ngIf="hasViewSwitch"
class="btn action-item action-button"
(click)="switchToLiveView()">
<i class="icon feather-image"></i>
<span class="text">Live view</span>
</button>
<nb-tabset class="tabs-container">
<nb-tab *ngFor="let example of examples" tabTitle="{{ example.extension }}" [active]="example.active">
<ngx-code-block [path]="example.path" [code]="example.code"></ngx-code-block>
</nb-tab>
</nb-tabset>

View file

@ -0,0 +1,84 @@
@import '../../../@theme/styles/themes';
@import '~@nebular/theme/styles/global/breakpoints';
@include nb-install-component() {
$tab-fg: nb-theme(color-fg-heading-light);
$tab-active-fg: #ffffff;
$tab-active-bg: linear-gradient(225deg, #333c66 0%, #1d2447 100%);
$tabs-bb: #ebeff5;
display: block;
position: relative;
button {
background: transparent;
color: $tab-fg;
text-transform: inherit;
padding: 0.45rem 1.5rem;
position: absolute;
right: 0;
top: 0;
cursor: pointer;
font-weight: normal;
font-size: 0.9rem;
.icon {
font-size: 0.95rem;
}
&:focus, &:active, &:hover {
cursor: pointer;
color: $tab-fg;
outline: 0;
}
.text {
display: none;
}
}
::ng-deep nb-tabset.tabs-container {
border-radius: 0.5rem 0.5rem 0 0;
> ul {
padding: 0;
margin-bottom: 0!important; // TODO: check selectors
border-radius: 0.5rem 0.5rem 0 0;
background-color: $tabs-bb;
overflow: hidden;
li {
padding: 0.4rem;
width: 20%;
margin-bottom: 0!important; // TODO: check selectors
&:first-child {
margin-left: 0;
}
a {
color: $tab-fg;
}
&.active {
background: $tab-active-bg;
a {
color: $tab-active-fg;
}
}
}
}
.container {
border-radius: 0 0 0.5rem 0.5rem;
}
}
@include media-breakpoint-up(is) {
button .text {
display: inline;
}
}
}

View file

@ -0,0 +1,52 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Input,
Output,
EventEmitter,
} from '@angular/core';
import { forkJoin, of as observableOf, Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { NgxExampleView } from '../../enum.example-view';
import { NgxCodeLoaderService } from '../../../@theme/services/code-loader.service';
@Component({
selector: 'ngx-tabbed-example-block',
styleUrls: ['./tabbed-example-block.component.scss'],
templateUrl: './tabbed-example-block.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxTabbedExampleBlockComponent {
@Input() hasViewSwitch = false;
@Output() changeView = new EventEmitter<NgxExampleView>();
examples = [];
@Input()
set content({ files }) {
forkJoin(files.map(file => this.load(file)))
.subscribe(loadedFiles => {
(loadedFiles[0] as any).active = true;
this.examples = loadedFiles;
this.cd.detectChanges();
});
}
constructor(private codeLoader: NgxCodeLoaderService, private cd: ChangeDetectorRef) {
}
switchToLiveView() {
this.changeView.emit(NgxExampleView.LIVE);
}
private load(path): Observable<any> {
const extension = path.split('.').pop();
return this.codeLoader.load(path)
.pipe(
map(code => ({ code, path, extension })),
catchError(e => observableOf('')),
);
}
}

View file

@ -0,0 +1,43 @@
<nb-card>
<nb-card-body>
<h2>{{ vm.themeTitle }} Theme</h2>
<p *ngIf="vm.parentTheme">inherited from {{ vm.parentTheme }} theme</p>
<div class="search-wrapper">
<input class="search-control" placeholder="Search for..." [formControl]="searchControl">
</div>
<table class="striped">
<thead>
<tr>
<td>Name</td>
<td>Value</td>
<td>Parent</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let prop of vm.filteredThemeProperties | async"
[ngxFragment]="prop.name"
[ngxFragmentSync]="false"
ngxFragmentClass="highlighted-row">
<td>
<a [routerLink]="" fragment="{{ prop.name }}">{{ prop.name }}</a>
</td>
<td ngxColorSwatch>{{ prop.value }}</td>
<td>
<a [routerLink]="['/docs/themes', parent.theme]" fragment="{{ parent.prop }}"
[class.inheritance-property]="index > 0"
*ngFor="let parent of prop.parents; let index = index">
<i *ngIf="index > 0" class="inheritance-icon feather-arrow-left"></i>
<span>{{ parent.prop }}</span>
<span *ngIf="parent.theme !== vm.themeName" class="parent-theme-name">({{ parent.theme }})</span>
</a>
</td>
</tr>
</tbody>
</table>
</nb-card-body>
</nb-card>

View file

@ -0,0 +1,40 @@
@import '../../../@theme/styles/themes';
@include nb-install-component() {
$inherited-fg: nb-theme(color-fg);
$search-fg: nb-theme(color-fg);
$search-bg: nb-theme(color-white);
$search-border: 1px solid nb-theme(color-gray-light);
$selected-row-bg: nb-theme(color-gray-light);
.inheritance-icon {
margin: 0 0.25rem;
}
.inheritance-property {
color: $inherited-fg;
}
.parent-theme-name {
margin-left: 0.25rem;
}
.highlighted-row {
background-color: $selected-row-bg !important;
}
.search-control {
display: block;
width: 100%;
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: $search-fg;
background-color: $search-bg;
background-clip: padding-box;
border: $search-border;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
}

View file

@ -0,0 +1,51 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import {
Component,
Input,
OnInit,
OnDestroy,
ChangeDetectionStrategy,
} from '@angular/core';
import { FormControl } from '@angular/forms';
import { takeWhile, skip, distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { ThemeBlockModel } from './theme-block.model';
import { ThemeBlockViewModel } from './theme-block.viewmodel';
@Component({
selector: 'ngx-theme-block',
styleUrls: ['./theme-block.component.scss'],
templateUrl: './theme-block.component.html',
providers: [ThemeBlockModel, ThemeBlockViewModel],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgxThemeComponent implements OnInit, OnDestroy {
searchControl = new FormControl();
private alive: boolean = true;
@Input('block')
set setBlock(block: any) {
this.vm.themeTitle = block.name;
this.vm.themeName = block.source.name;
this.vm.parentTheme = block.source.parent;
this.vm.themeProperties = block.source.data;
}
constructor(public vm: ThemeBlockViewModel) {}
ngOnInit() {
this.searchControl.valueChanges
.pipe(skip(1), distinctUntilChanged(), debounceTime(300), takeWhile(() => this.alive))
.subscribe(value => this.vm.changeSearch(value));
}
ngOnDestroy() {
this.alive = false;
}
}

View file

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
@Injectable()
export class ThemeBlockModel {
themeTitle: string;
themeName: string;
parentTheme: string;
themeProperties: any[];
setThemeTitle(value) {
this.themeTitle = value;
}
setThemeName(value) {
this.themeName = value;
}
setParentTheme(value) {
this.parentTheme = value;
}
setThemeProperties(value) {
this.themeProperties = value;
}
}

View file

@ -0,0 +1,69 @@
import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject, of as observableOf } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { ThemeBlockModel } from './theme-block.model';
@Injectable()
export class ThemeBlockViewModel {
private searchChanges$ = new BehaviorSubject<string>(null);
constructor(private model: ThemeBlockModel) {}
changeSearch(value) {
this.searchChanges$.next(value);
}
get themeTitle(): string {
return this.model.themeTitle;
}
set themeTitle(value) {
this.model.setThemeTitle(value);
}
get themeName(): string {
return this.model.themeName;
}
set themeName(value) {
this.model.setThemeName(value);
}
get parentTheme(): string {
return this.model.parentTheme;
}
set parentTheme(value) {
this.model.setParentTheme(value);
}
get themeProperties(): any[] {
return this.model.themeProperties;
}
set themeProperties(value) {
const result = Object.entries(value).map(([key, data]) => {
const propertyValue = data['value'];
return {
name: key,
value: Array.isArray(propertyValue) ? propertyValue.join(' ') : propertyValue,
parents: data['parents'],
};
});
this.model.setThemeProperties(result);
}
get filteredThemeProperties(): Observable<any[]> {
return this.searchChanges$.asObservable().pipe(
switchMap(value => {
if (value) {
return observableOf(
this.themeProperties.filter(({ name }) => name.toLowerCase().includes(value.toLowerCase())),
);
}
return observableOf(this.themeProperties);
}),
);
}
}

View file

@ -0,0 +1,4 @@
export enum NgxExampleView {
LIVE = 'live',
INLINE = 'inline',
}