mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-28 03:23:44 +03:00
Merge commit '71752aea02f8923e1e9fe7278ba7393c3ba751bf' into releases
This commit is contained in:
commit
e13c6309ce
65
CHANGELOG.md
65
CHANGELOG.md
|
@ -1,3 +1,68 @@
|
||||||
|
<a name="1.14.0"></a>
|
||||||
|
# [1.14.0](https://github.com/Rebilly/ReDoc/compare/v1.13.0...v1.14.0) (2017-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't show download button if initialized with an object ([476d6c4](https://github.com/Rebilly/ReDoc/commit/476d6c4))
|
||||||
|
* endpoint link doesn't expand when click on arrow ([9248cc2](https://github.com/Rebilly/ReDoc/commit/9248cc2))
|
||||||
|
* markdown block text color 💅 ([0f6f035](https://github.com/Rebilly/ReDoc/commit/0f6f035)), closes [#255](https://github.com/Rebilly/ReDoc/issues/255)
|
||||||
|
* ReDoc removes path if site is using history API ([c77e1a2](https://github.com/Rebilly/ReDoc/commit/c77e1a2)), closes [#257](https://github.com/Rebilly/ReDoc/issues/257)
|
||||||
|
* remove trailing slash from url when use `x-servers` ([2760a34](https://github.com/Rebilly/ReDoc/commit/2760a34))
|
||||||
|
* subscription leak in side-menu ([838f233](https://github.com/Rebilly/ReDoc/commit/838f233))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add GH-like anchors to h1 and h2 headings in md ([bb3667d](https://github.com/Rebilly/ReDoc/commit/bb3667d))
|
||||||
|
* add perfect-scrollbar for side menu ([cdeee67](https://github.com/Rebilly/ReDoc/commit/cdeee67))
|
||||||
|
* emphasize path with primary color in servers dropdown ([388b3d4](https://github.com/Rebilly/ReDoc/commit/388b3d4))
|
||||||
|
* new option `path-in-middle-panel` ([74a3193](https://github.com/Rebilly/ReDoc/commit/74a3193))
|
||||||
|
* SideMenu to support items template as a parameter ([8a49fb3](https://github.com/Rebilly/ReDoc/commit/8a49fb3))
|
||||||
|
|
||||||
|
|
||||||
|
<a name="1.13.0"></a>
|
||||||
|
# 1.13.0 (2017-04-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix issue with loading https spec ([585b9cf](https://github.com/Rebilly/ReDoc/commit/585b9cf)), closes [#243](https://github.com/Rebilly/ReDoc/issues/243) (by Khoa Tran)
|
||||||
|
* UL missing css ([303b49e](https://github.com/Rebilly/ReDoc/commit/303b49e)), closes [#248](https://github.com/Rebilly/ReDoc/issues/248)
|
||||||
|
* don't show contact info if it is empty object ([6077cc6](https://github.com/Rebilly/ReDoc/commit/6077cc6))
|
||||||
|
* code block formatting in markdown list ([a9cad19](https://github.com/Rebilly/ReDoc/commit/a9cad19)), closes [#242](https://github.com/Rebilly/ReDoc/issues/242)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* HTTP verbs badges in side menu ([92eec25](https://github.com/Rebilly/ReDoc/commit/92eec25)), closes [#61](https://github.com/Rebilly/ReDoc/issues/61)
|
||||||
|
* HTTP verbs badges in search results ([61fd426](https://github.com/Rebilly/ReDoc/commit/61fd426))
|
||||||
|
* new option [`no-auto-auth`](https://github.com/Rebilly/ReDoc#redoc-tag-attributes) to disable authentication section auto adding ([00b304a](https://github.com/Rebilly/ReDoc/commit/00b304a))
|
||||||
|
|
||||||
|
<a name="1.12.1"></a>
|
||||||
|
# 1.12.1 (2017-04-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix: use replace state instead of pushState ([4f4e748](https://github.com/Rebilly/ReDoc/commit/4f4e748)), closes [#244](https://github.com/Rebilly/ReDoc/issues/244)
|
||||||
|
|
||||||
|
<a name="1.12.0"></a>
|
||||||
|
# 1.12.0 (2017-04-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add safeguard for undefined ([aaac434](https://github.com/Rebilly/ReDoc/commit/aaac434)), closes [#236](https://github.com/Rebilly/ReDoc/issues/236)
|
||||||
|
* view errors were not reported ([6aa3a7d](https://github.com/Rebilly/ReDoc/commit/6aa3a7d))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Support x-examples vendor extension for requests (by [@brendo](https://github.com/brendo))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
* Updated to Angular 4, bundle is a bit smaller now
|
||||||
|
|
||||||
<a name="1.11.0"></a>
|
<a name="1.11.0"></a>
|
||||||
# 1.11.0 (2017-03-09)
|
# 1.11.0 (2017-03-09)
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,7 @@ ReDoc makes use of the following [vendor extensions](http://swagger.io/specifica
|
||||||
* `expand-responses` - specify which responses to expand by default by response codes. Values should be passed as comma-separated list without spaces e.g. `expand-responses="200,201"`. Special value `"all"` expands all responses by default. Be careful: this option can slow-down documentation rendering time.
|
* `expand-responses` - specify which responses to expand by default by response codes. Values should be passed as comma-separated list without spaces e.g. `expand-responses="200,201"`. Special value `"all"` expands all responses by default. Be careful: this option can slow-down documentation rendering time.
|
||||||
* `required-props-first` - show required properties first ordered in the same order as in `required` array.
|
* `required-props-first` - show required properties first ordered in the same order as in `required` array.
|
||||||
* `no-auto-auth` - do not inject Authentication section automatically
|
* `no-auto-auth` - do not inject Authentication section automatically
|
||||||
|
* `path-in-middle-panel` - show path link and HTTP verb in the middle panel instead of the right one
|
||||||
|
|
||||||
## Advanced usage
|
## Advanced usage
|
||||||
Instead of adding `spec-url` attribute to the `<redoc>` element you can initialize ReDoc via globally exposed `Redoc` object:
|
Instead of adding `spec-url` attribute to the `<redoc>` element you can initialize ReDoc via globally exposed `Redoc` object:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="api-info-wrapper">
|
<div class="api-info-wrapper">
|
||||||
<h1>{{info.title}} <span class="api-info-version">({{info.version}})</span></h1>
|
<h1>{{info.title}} <span class="api-info-version">({{info.version}})</span></h1>
|
||||||
<p>
|
<p class="donwload-openapi" *ngIf="specUrl">
|
||||||
Download OpenAPI (fka Swagger) specification:
|
Download OpenAPI (fka Swagger) specification:
|
||||||
<a class="openapi-button" target="_blank" attr.href='{{specUrl}}'> Download </a>
|
<a class="openapi-button" target="_blank" attr.href='{{specUrl}}'> Download </a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -23,7 +23,7 @@ export class ApiInfo extends BaseComponent implements OnInit {
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.info = this.componentSchema.info;
|
this.info = this.componentSchema.info;
|
||||||
this.specUrl = this.optionsService.options.specUrl;
|
this.specUrl = this.specMgr.specUrl;
|
||||||
if (!isNaN(parseInt(this.info.version.toString().substring(0, 1)))) {
|
if (!isNaN(parseInt(this.info.version.toString().substring(0, 1)))) {
|
||||||
this.info.version = 'v' + this.info.version;
|
this.info.version = 'v' + this.info.version;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
<span><!--
|
<span><!--
|
||||||
--><span class="operation-api-url-path">{{path}}</span><!--
|
--><span class="operation-api-url-path">{{path}}</span><!--
|
||||||
--></span>
|
--></span>
|
||||||
|
<svg class="expand-icon" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" viewBox="0 0 24 24" xml:space="preserve">
|
||||||
|
<polygon fill="white" points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<svg class="expand-icon" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" viewBox="0 0 24 24" xml:space="preserve">
|
|
||||||
<polygon fill="white" points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "/>
|
|
||||||
</svg>
|
|
||||||
<div class="servers-overlay">
|
<div class="servers-overlay">
|
||||||
<div *ngFor="let server of servers" class="server-item">
|
<div *ngFor="let server of servers" class="server-item">
|
||||||
<div class="description" [innerHtml]="server.description | marked"></div>
|
<div class="description" [innerHtml]="server.description | marked"></div>
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
border: 1px solid $border-color;
|
border: 1px solid $border-color;
|
||||||
background: $background-color;
|
background: $background-color;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
color: $primary-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { Component, ChangeDetectionStrategy, Input, OnInit, HostListener, HostBinding} from '@angular/core';
|
import { Component, ChangeDetectionStrategy, Input, OnInit, HostListener, HostBinding} from '@angular/core';
|
||||||
import { BaseComponent, SpecManager } from '../base';
|
import { BaseComponent, SpecManager } from '../base';
|
||||||
import { OptionsService } from '../../services/';
|
import { OptionsService } from '../../services/';
|
||||||
|
import { stripTrailingSlash } from '../../utils/';
|
||||||
|
|
||||||
export interface ServerInfo {
|
export interface ServerInfo {
|
||||||
description: string;
|
description: string;
|
||||||
|
@ -36,7 +37,7 @@ export class EndpointLink implements OnInit {
|
||||||
if (servers) {
|
if (servers) {
|
||||||
this.servers = servers.map(({url, description}) => ({
|
this.servers = servers.map(({url, description}) => ({
|
||||||
description,
|
description,
|
||||||
url: url.startsWith('//') ? `${this.specMgr.apiProtocol}:${url}` : url
|
url: stripTrailingSlash(url.startsWith('//') ? `${this.specMgr.apiProtocol}:${url}` : url)
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
this.servers = [
|
this.servers = [
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
<h2 class="operation-header sharable-header">
|
<h2 class="operation-header sharable-header">
|
||||||
<a class="share-link" href="#{{operation.anchor}}"></a>{{operation.summary}}
|
<a class="share-link" href="#{{operation.anchor}}"></a>{{operation.summary}}
|
||||||
</h2>
|
</h2>
|
||||||
|
<endpoint-link *ngIf="pathInMiddlePanel"
|
||||||
|
[verb]="operation.verb" [path]="operation.path"> </endpoint-link>
|
||||||
<div class="operation-tags" *ngIf="operation.info.tags.length">
|
<div class="operation-tags" *ngIf="operation.info.tags.length">
|
||||||
<a *ngFor="let tag of operation.info.tags" attr.href="#tag/{{tag}}"> {{tag}} </a>
|
<a *ngFor="let tag of operation.info.tags" attr.href="#tag/{{tag}}"> {{tag}} </a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,9 +16,9 @@
|
||||||
<responses-list pointer="{{pointer}}/responses"> </responses-list>
|
<responses-list pointer="{{pointer}}/responses"> </responses-list>
|
||||||
</div>
|
</div>
|
||||||
<div class="operation-samples">
|
<div class="operation-samples">
|
||||||
<h4 class="operation-params-subheader">Definition</h4>
|
|
||||||
|
|
||||||
<endpoint-link [verb]="operation.verb" [path]="operation.path"> </endpoint-link>
|
<endpoint-link *ngIf="!pathInMiddlePanel"
|
||||||
|
[verb]="operation.verb" [path]="operation.path"> </endpoint-link>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<request-samples [pointer]="pointer" [schemaPointer]="operation.bodyParam?._pointer">
|
<request-samples [pointer]="pointer" [schemaPointer]="operation.bodyParam?._pointer">
|
||||||
|
|
|
@ -57,6 +57,10 @@
|
||||||
background: $samples-panel-bg-color;
|
background: $samples-panel-bg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operation-samples pre {
|
||||||
|
color: $sample-panel-color;
|
||||||
|
}
|
||||||
|
|
||||||
.operation-samples header,
|
.operation-samples header,
|
||||||
.operation-samples > h5 {
|
.operation-samples > h5 {
|
||||||
color: $sample-panel-headers-color;
|
color: $sample-panel-headers-color;
|
||||||
|
@ -103,3 +107,28 @@
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operation-content /deep/ endpoint-link {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.operation-endpoint[class] {
|
||||||
|
padding: 5px 30px 5px 5px;
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
border-radius: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.operation-api-url-path {
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-icon {
|
||||||
|
top: 8px;
|
||||||
|
background-color: $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.servers-overlay {
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -35,12 +35,15 @@ export class Operation extends BaseComponent implements OnInit {
|
||||||
@HostBinding('attr.operation-id') operationId;
|
@HostBinding('attr.operation-id') operationId;
|
||||||
|
|
||||||
operation: OperationInfo;
|
operation: OperationInfo;
|
||||||
|
pathInMiddlePanel: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
specMgr:SpecManager,
|
specMgr:SpecManager,
|
||||||
private optionsService: OptionsService,
|
private optionsService: OptionsService,
|
||||||
private menu: MenuService) {
|
private menu: MenuService) {
|
||||||
super(specMgr);
|
super(specMgr);
|
||||||
|
|
||||||
|
this.pathInMiddlePanel = optionsService.options.pathInMiddlePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
side-menu {
|
side-menu {
|
||||||
overflow-y: auto;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
[sticky-sidebar] {
|
[sticky-sidebar] {
|
||||||
|
@ -264,6 +264,7 @@ footer {
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
color: white;
|
||||||
|
|
||||||
&:before, &:after {
|
&:before, &:after {
|
||||||
content: none;
|
content: none;
|
||||||
|
|
|
@ -62,7 +62,7 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
li.menu-item-depth-1 {
|
li.menu-item-depth-1 {
|
||||||
color: #0033a0;
|
color: $primary-color;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
-o-transition: all .15s ease-in-out;
|
-o-transition: all .15s ease-in-out;
|
||||||
transition: all .15s ease-in-out;
|
transition: all .15s ease-in-out;
|
||||||
display: block;
|
display: block;
|
||||||
|
margin: 0;
|
||||||
padding: $side-menu-item-vpadding*2.5 $side-menu-item-hpadding;
|
padding: $side-menu-item-vpadding*2.5 $side-menu-item-hpadding;
|
||||||
|
|
||||||
&[hidden] {
|
&[hidden] {
|
||||||
|
@ -33,11 +34,6 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item:hover,
|
|
||||||
.menu-item.active {
|
|
||||||
//background: darken($side-menu-active-bg-color, 6%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-subitems {
|
.menu-subitems {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
|
|
|
@ -4,8 +4,14 @@
|
||||||
<span class="selected-endpoint">{{activeItemCaption}}</span>
|
<span class="selected-endpoint">{{activeItemCaption}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div #desktop id="resources-nav">
|
<ng-template #default>
|
||||||
|
<side-menu-items [items]="menuItems" (activate)="activateAndScroll($event)"></side-menu-items>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div #desktop id="resources-nav" perfect-scrollbar>
|
||||||
<ul class="menu-root">
|
<ul class="menu-root">
|
||||||
<side-menu-items [items]="menuItems" (activate)="activateAndScroll($event)"></side-menu-items>
|
<div *ngIf="itemsTemplate; else default">
|
||||||
|
<ng-container *ngTemplateOutlet="itemsTemplate; context: this"></ng-container>
|
||||||
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
$mobile-menu-compact-breakpoint: 550px;
|
$mobile-menu-compact-breakpoint: 550px;
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: flex;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#resources-nav {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
ul.menu-root {
|
ul.menu-root {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -35,6 +39,10 @@ ul.menu-root {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $side-menu-mobile-breakpoint) {
|
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.mobile-nav {
|
.mobile-nav {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@ import { Component,
|
||||||
Output,
|
Output,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
|
ViewChild,
|
||||||
OnInit,
|
OnInit,
|
||||||
OnDestroy
|
OnDestroy
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { trigger, state, animate, transition, style } from '@angular/core';
|
import { trigger, state, animate, transition, style } from '@angular/core';
|
||||||
import { ScrollService, MenuService, OptionsService, MenuItem } from '../../services/';
|
import { ScrollService, MenuService, OptionsService, MenuItem } from '../../services/';
|
||||||
|
import { PerfectScrollbar } from '../../shared/components';
|
||||||
import { BrowserDomAdapter as DOM } from '../../utils/browser-adapter';
|
import { BrowserDomAdapter as DOM } from '../../utils/browser-adapter';
|
||||||
|
|
||||||
const global = window;
|
const global = window;
|
||||||
|
@ -50,6 +52,8 @@ export class SideMenu implements OnInit, OnDestroy {
|
||||||
activeCatCaption: string;
|
activeCatCaption: string;
|
||||||
activeItemCaption: string;
|
activeItemCaption: string;
|
||||||
menuItems: Array<MenuItem>;
|
menuItems: Array<MenuItem>;
|
||||||
|
@Input() itemsTemplate;
|
||||||
|
@ViewChild(PerfectScrollbar) PS:PerfectScrollbar;
|
||||||
|
|
||||||
private options: any;
|
private options: any;
|
||||||
private $element: any;
|
private $element: any;
|
||||||
|
@ -57,6 +61,9 @@ export class SideMenu implements OnInit, OnDestroy {
|
||||||
private $resourcesNav: any;
|
private $resourcesNav: any;
|
||||||
private $scrollParent: any;
|
private $scrollParent: any;
|
||||||
|
|
||||||
|
private changedActiveSubscription;
|
||||||
|
private changedSubscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
elementRef:ElementRef,
|
elementRef:ElementRef,
|
||||||
private scrollService:ScrollService,
|
private scrollService:ScrollService,
|
||||||
|
@ -71,8 +78,10 @@ export class SideMenu implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.options = optionsService.options;
|
this.options = optionsService.options;
|
||||||
|
|
||||||
this.menuService.changedActiveItem.subscribe((evt) => this.changed(evt));
|
this.changedActiveSubscription = this.menuService.changedActiveItem.subscribe((evt) => this.changed(evt));
|
||||||
this.menuService.changed.subscribe((evt) => this.detectorRef.detectChanges());
|
this.changedSubscription = this.menuService.changed.subscribe((evt) => {
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
changed(item) {
|
changed(item) {
|
||||||
|
@ -89,11 +98,16 @@ export class SideMenu implements OnInit, OnDestroy {
|
||||||
this.activeItemCaption = '';
|
this.activeItemCaption = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
//safari doesn't update bindings if not run changeDetector manually :(
|
// safari doesn't update bindings if not run changeDetector manually :(
|
||||||
this.detectorRef.detectChanges();
|
this.update();
|
||||||
this.scrollActiveIntoView();
|
this.scrollActiveIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.detectorRef.detectChanges();
|
||||||
|
this.PS && this.PS.update();
|
||||||
|
}
|
||||||
|
|
||||||
scrollActiveIntoView() {
|
scrollActiveIntoView() {
|
||||||
let $item = this.$element.querySelector('li.active, label.active');
|
let $item = this.$element.querySelector('li.active, label.active');
|
||||||
if ($item) $item.scrollIntoViewIfNeeded();
|
if ($item) $item.scrollIntoViewIfNeeded();
|
||||||
|
@ -141,6 +155,8 @@ export class SideMenu implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
this.changedActiveSubscription.unsubscribe();
|
||||||
|
this.changedSubscription.unsubscribe();
|
||||||
this.scrollService.unbind();
|
this.scrollService.unbind();
|
||||||
this.menuService.destroy();
|
this.menuService.destroy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import { NgModule, ErrorHandler, APP_ID } from '@angular/core';
|
import { NgModule, ErrorHandler, APP_ID } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { SpecManager } from './utils/spec-manager';
|
||||||
|
|
||||||
import { Redoc, SecurityDefinitions, Operation, REDOC_DIRECTIVES } from './components/index';
|
import { Redoc, SecurityDefinitions, Operation, REDOC_DIRECTIVES } from './components/index';
|
||||||
import { REDOC_COMMON_DIRECTIVES, DynamicNg2Wrapper } from './shared/components/index';
|
import { REDOC_COMMON_DIRECTIVES, DynamicNg2Wrapper, DropDown } from './shared/components/index';
|
||||||
import { REDOC_PIPES } from './utils/pipes';
|
import { REDOC_PIPES } from './utils/pipes';
|
||||||
import { CustomErrorHandler } from './utils/'
|
import { CustomErrorHandler } from './utils/'
|
||||||
import { LazyTasksService } from './shared/components/LazyFor/lazy-for';
|
import { LazyTasksService } from './shared/components/LazyFor/lazy-for';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
OptionsService,
|
OptionsService,
|
||||||
|
Options,
|
||||||
MenuService,
|
MenuService,
|
||||||
ScrollService,
|
ScrollService,
|
||||||
Hash,
|
Hash,
|
||||||
|
@ -19,10 +22,9 @@ import {
|
||||||
Marker,
|
Marker,
|
||||||
SchemaHelper,
|
SchemaHelper,
|
||||||
SearchService,
|
SearchService,
|
||||||
|
MenuItem,
|
||||||
COMPONENT_PARSER_ALLOWED } from './services/';
|
COMPONENT_PARSER_ALLOWED } from './services/';
|
||||||
|
|
||||||
import { SpecManager } from './utils/spec-manager';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [ CommonModule ],
|
imports: [ CommonModule ],
|
||||||
declarations: [ REDOC_DIRECTIVES, REDOC_COMMON_DIRECTIVES, REDOC_PIPES ],
|
declarations: [ REDOC_DIRECTIVES, REDOC_COMMON_DIRECTIVES, REDOC_PIPES ],
|
||||||
|
@ -49,6 +51,7 @@ export { Redoc, SpecManager, ScrollService,
|
||||||
Hash,
|
Hash,
|
||||||
WarningsService,
|
WarningsService,
|
||||||
OptionsService,
|
OptionsService,
|
||||||
|
Options,
|
||||||
AppStateService,
|
AppStateService,
|
||||||
ComponentParser,
|
ComponentParser,
|
||||||
ContentProjector,
|
ContentProjector,
|
||||||
|
@ -56,4 +59,5 @@ MenuService,
|
||||||
SearchService,
|
SearchService,
|
||||||
SchemaHelper,
|
SchemaHelper,
|
||||||
LazyTasksService,
|
LazyTasksService,
|
||||||
Marker };
|
MenuItem,
|
||||||
|
Marker, DropDown };
|
||||||
|
|
|
@ -30,7 +30,7 @@ export class Hash {
|
||||||
update(hash: string|null, rewriteHistory:boolean = false) {
|
update(hash: string|null, rewriteHistory:boolean = false) {
|
||||||
if (hash == undefined) return;
|
if (hash == undefined) return;
|
||||||
if (rewriteHistory) {
|
if (rewriteHistory) {
|
||||||
window.history.replaceState(null, '', '#' + hash);
|
window.history.replaceState(null, '', window.location.href.split('#')[0] + '#' + hash);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.noEmit = true;
|
this.noEmit = true;
|
||||||
|
|
|
@ -104,7 +104,7 @@ export class MenuService {
|
||||||
|
|
||||||
// check if previous items§ can be enabled
|
// check if previous items§ can be enabled
|
||||||
let prevItem = this.flatItems[idx -= 1];
|
let prevItem = this.flatItems[idx -= 1];
|
||||||
while(prevItem && (!prevItem.metadata || !prevItem.items)) {
|
while(prevItem && (!prevItem.metadata || prevItem.metadata.type === 'heading' || !prevItem.items)) {
|
||||||
prevItem.ready = true;
|
prevItem.ready = true;
|
||||||
prevItem = this.flatItems[idx -= 1];
|
prevItem = this.flatItems[idx -= 1];
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ export class MenuService {
|
||||||
// We only need to go up the chain for operations that
|
// We only need to go up the chain for operations that
|
||||||
// might have multiple tags. For headers/subheaders
|
// might have multiple tags. For headers/subheaders
|
||||||
// we need to siply early terminate.
|
// we need to siply early terminate.
|
||||||
if (!currentItem.metadata) {
|
if (!currentItem.metadata || currentItem.metadata.type === 'heading') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,10 @@ export class MenuService {
|
||||||
let item = {
|
let item = {
|
||||||
name: heading.title,
|
name: heading.title,
|
||||||
id: id,
|
id: id,
|
||||||
items: null
|
items: null,
|
||||||
|
metadata: {
|
||||||
|
type: 'heading'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
item.items = this.getMarkdownSubheaders(item, heading);
|
item.items = this.getMarkdownSubheaders(item, heading);
|
||||||
|
|
||||||
|
@ -309,7 +312,10 @@ export class MenuService {
|
||||||
let subItem = {
|
let subItem = {
|
||||||
name: heading.title,
|
name: heading.title,
|
||||||
id: id,
|
id: id,
|
||||||
parent: parent
|
parent: parent,
|
||||||
|
metadata: {
|
||||||
|
type: 'heading'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
res.push(subItem);
|
res.push(subItem);
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,8 @@ const OPTION_NAMES = new Set([
|
||||||
'lazyRendering',
|
'lazyRendering',
|
||||||
'expandResponses',
|
'expandResponses',
|
||||||
'requiredPropsFirst',
|
'requiredPropsFirst',
|
||||||
'noAutoAuth'
|
'noAutoAuth',
|
||||||
|
'pathInMiddlePanel',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
|
@ -31,6 +32,7 @@ export interface Options {
|
||||||
$scrollParent?: HTMLElement | Window;
|
$scrollParent?: HTMLElement | Window;
|
||||||
requiredPropsFirst?: boolean;
|
requiredPropsFirst?: boolean;
|
||||||
noAutoAuth?: boolean;
|
noAutoAuth?: boolean;
|
||||||
|
pathInMiddlePanel?: boolean;
|
||||||
spec?: any;
|
spec?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +100,7 @@ export class OptionsService {
|
||||||
if (isString(this._options.lazyRendering)) this._options.lazyRendering = true;
|
if (isString(this._options.lazyRendering)) this._options.lazyRendering = true;
|
||||||
if (isString(this._options.requiredPropsFirst)) this._options.requiredPropsFirst = true;
|
if (isString(this._options.requiredPropsFirst)) this._options.requiredPropsFirst = true;
|
||||||
if (isString(this._options.noAutoAuth)) this._options.noAutoAuth = true;
|
if (isString(this._options.noAutoAuth)) this._options.noAutoAuth = true;
|
||||||
|
if (isString(this._options.pathInMiddlePanel)) this._options.pathInMiddlePanel = true;
|
||||||
if (isString(this._options.expandResponses)) {
|
if (isString(this._options.expandResponses)) {
|
||||||
let str = this._options.expandResponses as string;
|
let str = this._options.expandResponses as string;
|
||||||
if (str === 'all') return;
|
if (str === 'all') return;
|
||||||
|
|
37
lib/shared/components/PerfectScrollbar/perfect-scrollbar.ts
Normal file
37
lib/shared/components/PerfectScrollbar/perfect-scrollbar.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import 'perfect-scrollbar/dist/css/perfect-scrollbar.css';
|
||||||
|
|
||||||
|
import { Directive, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';
|
||||||
|
import { BrowserDomAdapter as DOM } from '../../../utils/browser-adapter';
|
||||||
|
|
||||||
|
import * as PS from 'perfect-scrollbar';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[perfect-scrollbar]'
|
||||||
|
})
|
||||||
|
export class PerfectScrollbar implements OnInit, OnDestroy {
|
||||||
|
$element: any;
|
||||||
|
subscription: any;
|
||||||
|
|
||||||
|
constructor(elementRef:ElementRef) {
|
||||||
|
this.$element = elementRef.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
PS.update(this.$element);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
requestAnimationFrame(() => PS.initialize(this.$element, {
|
||||||
|
wheelSpeed: 2,
|
||||||
|
wheelPropagation: false,
|
||||||
|
minScrollbarLength: 20,
|
||||||
|
suppressScrollX: true
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
PS.destroy(this.$element);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,10 +7,11 @@ import { CopyButton } from './CopyButton/copy-button.directive';
|
||||||
import { SelectOnClick } from './SelectOnClick/select-on-click.directive';
|
import { SelectOnClick } from './SelectOnClick/select-on-click.directive';
|
||||||
import { DynamicNg2Viewer, DynamicNg2Wrapper } from './DynamicNg2Viewer/dynamic-ng2-viewer.component';
|
import { DynamicNg2Viewer, DynamicNg2Wrapper } from './DynamicNg2Viewer/dynamic-ng2-viewer.component';
|
||||||
import { LazyFor, LazyTasksService, LazyTasksServiceSync } from './LazyFor/lazy-for';
|
import { LazyFor, LazyTasksService, LazyTasksServiceSync } from './LazyFor/lazy-for';
|
||||||
|
import { PerfectScrollbar } from './PerfectScrollbar/perfect-scrollbar';
|
||||||
|
|
||||||
export const REDOC_COMMON_DIRECTIVES = [
|
export const REDOC_COMMON_DIRECTIVES = [
|
||||||
DropDown, StickySidebar, Tabs, Tab, Zippy, CopyButton, SelectOnClick, DynamicNg2Viewer, DynamicNg2Wrapper, LazyFor
|
PerfectScrollbar, DropDown, StickySidebar, Tabs, Tab, Zippy, CopyButton, SelectOnClick, DynamicNg2Viewer, DynamicNg2Wrapper, LazyFor
|
||||||
];
|
];
|
||||||
|
|
||||||
export { DropDown, StickySidebar, Tabs, Tab, Zippy, CopyButton, SelectOnClick, DynamicNg2Viewer, DynamicNg2Wrapper, LazyFor }
|
export { DropDown, StickySidebar, Tabs, Tab, Zippy, CopyButton, SelectOnClick, DynamicNg2Viewer, DynamicNg2Wrapper, LazyFor }
|
||||||
export { LazyTasksService, LazyTasksServiceSync }
|
export { LazyTasksService, LazyTasksServiceSync, PerfectScrollbar }
|
||||||
|
|
|
@ -20,6 +20,10 @@ export function isBlank(obj: any): boolean {
|
||||||
return obj == undefined;
|
return obj == undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function stripTrailingSlash(path:string):string {
|
||||||
|
return path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
||||||
|
}
|
||||||
|
|
||||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||||
export function groupBy<T>(array: T[], key:string):StringMap<T[]> {
|
export function groupBy<T>(array: T[], key:string):StringMap<T[]> {
|
||||||
return array.reduce<StringMap<T[]>>(function(res, value) {
|
return array.reduce<StringMap<T[]>>(function(res, value) {
|
||||||
|
@ -119,7 +123,7 @@ export function isJsonLike(contentType: string): boolean {
|
||||||
return contentType.search(/json/i) !== -1;
|
return contentType.search(/json/i) !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getJsonLike(object: object) {
|
export function getJsonLike(object: Object) {
|
||||||
const jsonLikeKeys = Object.keys(object).filter(isJsonLike);
|
const jsonLikeKeys = Object.keys(object).filter(isJsonLike);
|
||||||
|
|
||||||
if (!jsonLikeKeys.length) {
|
if (!jsonLikeKeys.length) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ const md = new Remarkable({
|
||||||
export interface MarkdownHeading {
|
export interface MarkdownHeading {
|
||||||
title?: string;
|
title?: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
slug?: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
children?: StringMap<MarkdownHeading>;
|
children?: StringMap<MarkdownHeading>;
|
||||||
}
|
}
|
||||||
|
@ -52,12 +53,14 @@ export class MdRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
saveHeading(title: string, parent:MarkdownHeading = {id:null, children: this.headings}) :MarkdownHeading {
|
saveHeading(title: string, parent:MarkdownHeading = {id:null, children: this.headings}) :MarkdownHeading {
|
||||||
let id = slugify(title);
|
let slug = slugify(title);
|
||||||
|
let id = slug;
|
||||||
if (parent && parent.id) id = `${parent.id}/${id}`;
|
if (parent && parent.id) id = `${parent.id}/${id}`;
|
||||||
parent.children = parent.children || {};
|
parent.children = parent.children || {};
|
||||||
parent.children[id] = {
|
parent.children[id] = {
|
||||||
title,
|
title,
|
||||||
id
|
id,
|
||||||
|
slug
|
||||||
};
|
};
|
||||||
return parent.children[id];
|
return parent.children[id];
|
||||||
}
|
}
|
||||||
|
@ -110,12 +113,14 @@ export class MdRenderer {
|
||||||
this.currentTopHeading = this.saveHeading(content);;
|
this.currentTopHeading = this.saveHeading(content);;
|
||||||
let id = this.currentTopHeading.id;
|
let id = this.currentTopHeading.id;
|
||||||
return `<h${tokens[idx].hLevel} section="section/${id}">` +
|
return `<h${tokens[idx].hLevel} section="section/${id}">` +
|
||||||
`<a class="share-link" href="#section/${id}"></a>`;
|
`<a class="share-link" href="#section/${id}"></a>` +
|
||||||
|
`<a name="${id.toLowerCase()}"></a>`;
|
||||||
} else if (tokens[idx].hLevel === 2 ) {
|
} else if (tokens[idx].hLevel === 2 ) {
|
||||||
let heading = this.saveHeading(content, this.currentTopHeading);
|
let heading = this.saveHeading(content, this.currentTopHeading);
|
||||||
let contentSlug = `${heading.id}`;
|
let contentSlug = `${heading.id}`;
|
||||||
return `<h${tokens[idx].hLevel} section="section/${heading.id}">` +
|
return `<h${tokens[idx].hLevel} section="section/${heading.id}">` +
|
||||||
`<a class="share-link" href="#section/${contentSlug}"></a>`;
|
`<a class="share-link" href="#section/${contentSlug}"></a>` +
|
||||||
|
`<a name="${heading.slug.toLowerCase()}"></a>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class SpecManager {
|
||||||
public basePath: string;
|
public basePath: string;
|
||||||
|
|
||||||
public spec = new BehaviorSubject<any|null>(null);
|
public spec = new BehaviorSubject<any|null>(null);
|
||||||
public _specUrl: string;
|
public specUrl: string;
|
||||||
private parser: any;
|
private parser: any;
|
||||||
private options: Options;
|
private options: Options;
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export class SpecManager {
|
||||||
this.parser.bundle(urlOrObject, {http: {withCredentials: false}})
|
this.parser.bundle(urlOrObject, {http: {withCredentials: false}})
|
||||||
.then(schema => {
|
.then(schema => {
|
||||||
if (typeof urlOrObject === 'string') {
|
if (typeof urlOrObject === 'string') {
|
||||||
this._specUrl = urlOrObject;
|
this.specUrl = urlOrObject;
|
||||||
}
|
}
|
||||||
this._schema = snapshot(schema);
|
this._schema = snapshot(schema);
|
||||||
try {
|
try {
|
||||||
|
@ -64,7 +64,7 @@ export class SpecManager {
|
||||||
|
|
||||||
/* calculate common used values */
|
/* calculate common used values */
|
||||||
init() {
|
init() {
|
||||||
let urlParts = this._specUrl ? urlParse(urlResolve(window.location.href, this._specUrl)) : {};
|
let urlParts = this.specUrl ? urlParse(urlResolve(window.location.href, this.specUrl)) : {};
|
||||||
let schemes = this._schema.schemes;
|
let schemes = this._schema.schemes;
|
||||||
let protocol;
|
let protocol;
|
||||||
if (!schemes || !schemes.length) {
|
if (!schemes || !schemes.length) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"description": "Swagger-generated API Reference Documentation",
|
"description": "Swagger-generated API Reference Documentation",
|
||||||
"version": "1.13.0",
|
"version": "1.14.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/Rebilly/ReDoc"
|
"url": "git://github.com/Rebilly/ReDoc"
|
||||||
|
@ -124,5 +124,7 @@
|
||||||
"webpack-merge": "^4.1.0",
|
"webpack-merge": "^4.1.0",
|
||||||
"zone.js": "^0.8.5"
|
"zone.js": "^0.8.5"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {
|
||||||
|
"perfect-scrollbar": "^0.6.16"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,21 +46,21 @@ describe('Utils', () => {
|
||||||
|
|
||||||
it('should substitute api scheme when spec schemes are undefined', () => {
|
it('should substitute api scheme when spec schemes are undefined', () => {
|
||||||
specMgr._schema.schemes = undefined;
|
specMgr._schema.schemes = undefined;
|
||||||
specMgr._specUrl = 'https://petstore.swagger.io/v2';
|
specMgr.specUrl = 'https://petstore.swagger.io/v2';
|
||||||
specMgr.init();
|
specMgr.init();
|
||||||
specMgr.apiUrl.should.be.equal('https://petstore.swagger.io/v2');
|
specMgr.apiUrl.should.be.equal('https://petstore.swagger.io/v2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should substitute api host when spec host is undefined', () => {
|
it('should substitute api host when spec host is undefined', () => {
|
||||||
specMgr._schema.host = undefined;
|
specMgr._schema.host = undefined;
|
||||||
specMgr._specUrl = 'http://petstore.swagger.io/v2';
|
specMgr.specUrl = 'http://petstore.swagger.io/v2';
|
||||||
specMgr.init();
|
specMgr.init();
|
||||||
specMgr.apiUrl.should.be.equal('http://petstore.swagger.io/v2');
|
specMgr.apiUrl.should.be.equal('http://petstore.swagger.io/v2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use empty basePath when basePath is not present', () => {
|
it('should use empty basePath when basePath is not present', () => {
|
||||||
specMgr._schema.basePath = undefined;
|
specMgr._schema.basePath = undefined;
|
||||||
specMgr._specUrl = 'https://petstore.swagger.io';
|
specMgr.specUrl = 'https://petstore.swagger.io';
|
||||||
specMgr.init();
|
specMgr.init();
|
||||||
specMgr.basePath.should.be.equal('');
|
specMgr.basePath.should.be.equal('');
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
],
|
],
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"lib": [
|
"lib": [
|
||||||
"DOM", "ES2016", "DOM.Iterable"
|
"dom", "es2016", "dom.iterable"
|
||||||
],
|
],
|
||||||
"noEmitHelpers": false
|
"noEmitHelpers": false
|
||||||
},
|
},
|
||||||
|
|
|
@ -3782,6 +3782,10 @@ pend@~1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||||
|
|
||||||
|
perfect-scrollbar@^0.6.16:
|
||||||
|
version "0.6.16"
|
||||||
|
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-0.6.16.tgz#b1d61a5245cf3962bb9a8407a3fc669d923212fc"
|
||||||
|
|
||||||
phantomjs-prebuilt@^2.1.7:
|
phantomjs-prebuilt@^2.1.7:
|
||||||
version "2.1.14"
|
version "2.1.14"
|
||||||
resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb24014558f1188c516da0"
|
resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb24014558f1188c516da0"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user