mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-24 09:33:44 +03:00
Merge branch 'master' into releases
This commit is contained in:
commit
35651acd10
85
README.md
85
README.md
|
@ -7,10 +7,91 @@
|
||||||
|
|
||||||
Swagger-generated API Reference Documentation
|
Swagger-generated API Reference Documentation
|
||||||
|
|
||||||
**Under development**
|
|
||||||
|
|
||||||
[Live demo](http://rebilly.github.io/ReDoc/)
|
[Live demo](http://rebilly.github.io/ReDoc/)
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
## tl;dr
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>ReDoc</title>
|
||||||
|
<!-- needed for adaptive design -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ReDoc uses font options from the parent element
|
||||||
|
So override default browser styles
|
||||||
|
-->
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: Verdana, Geneva, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<redoc spec-url='http://petstore.swagger.io/v2/swagger.json'>
|
||||||
|
</redoc>
|
||||||
|
<script src="bower_components/redoc/dist/redoc.min.js"> </script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1. Install redoc
|
||||||
|
Install using [bower](bower.io):
|
||||||
|
|
||||||
|
bower install redoc
|
||||||
|
|
||||||
|
or using [npm](https://docs.npmjs.com/getting-started/what-is-npm):
|
||||||
|
|
||||||
|
npm install redoc --save
|
||||||
|
|
||||||
|
Alternatively you can just download [`redoc.min.js`](https://raw.githubusercontent.com/Rebilly/ReDoc/releases/dist/redoc.min.js).
|
||||||
|
|
||||||
|
#### 2. Reference redoc script in HTML
|
||||||
|
Then reference [`redoc.min.js`](https://raw.githubusercontent.com/Rebilly/ReDoc/releases/dist/redoc.min.js) in your HTML page:
|
||||||
|
```html
|
||||||
|
<script src="bower_components/redoc/dist/redoc.min.js"> </script>
|
||||||
|
```
|
||||||
|
For npm:
|
||||||
|
```html
|
||||||
|
<script src="node_modules/redoc/dist/redoc.min.js"> </script>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Add `<redoc>` element to your page
|
||||||
|
```html
|
||||||
|
<redoc spec-url="<url to your spec>"></redoc>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Enjoy :smile:
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
* `spec-url` - relative or absolute url to your spec file
|
||||||
|
* `scroll-y-offset` - If set, specifies a vertical scroll-offset. This is often useful when there are fixed positioned elements at the top of the page, such as navbars, headers etc.
|
||||||
|
`scroll-y-offset` can be specified in various ways:
|
||||||
|
* **number**: A fixed number of pixels to be used as offset
|
||||||
|
* **selector**: selector of the element to be used for specifying the offset. The distance from the top of the page to the element's bottom will be used as offset.
|
||||||
|
* **function**: A getter function. Must return a number representing the offset (in pixels).
|
||||||
|
|
||||||
|
## Advanced usage
|
||||||
|
Instead of adding `spec-url` attribute to the `<redoc>` element you can initialize ReDoc via globally exposed `Redoc` object:
|
||||||
|
```js
|
||||||
|
Redoc.init(specUrl, options)
|
||||||
|
```
|
||||||
|
|
||||||
|
`options` is javascript object with camel-cased versions of options names as the keys. For example:
|
||||||
|
```js
|
||||||
|
Redoc.init('http://petstore.swagger.io/v2/swagger.json', {
|
||||||
|
scrollYOffset: 50
|
||||||
|
})
|
||||||
|
```
|
||||||
|
-----------
|
||||||
## Running locally
|
## Running locally
|
||||||
1. Clone repository
|
1. Clone repository
|
||||||
`git clone https://github.com/Rebilly/ReDoc.git`
|
`git clone https://github.com/Rebilly/ReDoc.git`
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"description": "Swagger-generated API Reference Documentation",
|
"description": "Swagger-generated API Reference Documentation",
|
||||||
"main": "dist/redoc.full.min.js",
|
"main": "dist/redoc.min.js",
|
||||||
"authors": [
|
"authors": [
|
||||||
"Roman Hotsiy"
|
"Roman Hotsiy"
|
||||||
],
|
],
|
||||||
|
|
|
@ -3,7 +3,7 @@ module.exports = {
|
||||||
html: 'lib/**/*.html',
|
html: 'lib/**/*.html',
|
||||||
scss: 'lib/**/*.scss',
|
scss: 'lib/**/*.scss',
|
||||||
sourceEntryPoint: 'lib/index.js',
|
sourceEntryPoint: 'lib/index.js',
|
||||||
outputName: 'redoc.full',
|
outputName: 'redoc',
|
||||||
output: 'dist/',
|
output: 'dist/',
|
||||||
tmp: '.tmp/',
|
tmp: '.tmp/',
|
||||||
demo: 'demo/**/*',
|
demo: 'demo/**/*',
|
||||||
|
|
|
@ -55,7 +55,7 @@ var JS_DEV_DEPS_MIN = [
|
||||||
|
|
||||||
gulp.task('sass', function () {
|
gulp.task('sass', function () {
|
||||||
return gulp.src(paths.scss, { base: './' })
|
return gulp.src(paths.scss, { base: './' })
|
||||||
.pipe(sass.sync().on('error', sass.logError))
|
.pipe(sass.sync({outputStyle: 'compressed'}).on('error', sass.logError))
|
||||||
.pipe(gulp.dest(paths.tmp));
|
.pipe(gulp.dest(paths.tmp));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,21 +3,21 @@
|
||||||
<head>
|
<head>
|
||||||
<title>ReDoc</title>
|
<title>ReDoc</title>
|
||||||
<link rel="stylesheet" href="main.css">
|
<link rel="stylesheet" href="main.css">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
<header> ReDoc </header>
|
<header> ReDoc </header>
|
||||||
<input id="schema-url-input" value='swagger.json'>
|
<form id="schema-url-form">
|
||||||
<button id="load-button"> Explore </button>
|
<input id="schema-url-input" value='swagger.json'>
|
||||||
</nav>
|
<button type="submit"> Explore </button>
|
||||||
|
</form>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<redoc scroll-y-offset="body > nav" spec-url='swagger.json'>
|
<redoc scroll-y-offset="body > nav" spec-url='swagger.json'></redoc>
|
||||||
Loading...
|
|
||||||
</redoc>
|
|
||||||
|
|
||||||
<!-- ReDoc built file with all dependencies included -->
|
<!-- ReDoc built file with all dependencies included -->
|
||||||
<script src="dist/redoc.full.js"> </script>
|
<script src="dist/redoc.js"> </script>
|
||||||
<script src="main.js"> </script>
|
<script src="main.js"> </script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -19,21 +19,55 @@ nav header {
|
||||||
float: left;
|
float: left;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
color: white;
|
color: #00329F;
|
||||||
position: absolute;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav input {
|
nav input {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
|
|
||||||
|
color: #555;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
||||||
|
-webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
|
||||||
|
-o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
|
||||||
|
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav input:focus {
|
||||||
|
border-color: #66afe9;
|
||||||
|
outline: 0;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav button {
|
nav button {
|
||||||
border: 1px solid #FFFFFF;
|
background-color: #fff;
|
||||||
cursor: pointer;
|
color: #333;
|
||||||
color: white;
|
|
||||||
background-color: #21476D;
|
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
|
|
||||||
|
touch-action: manipulation;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav button:hover {
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
border-color: #adadad;
|
||||||
|
}
|
||||||
|
nav button:active {
|
||||||
|
background-color: #d4d4d4;
|
||||||
|
border-color: #8c8c8c;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
|
@ -41,8 +75,25 @@ nav {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: #053361;
|
background-color: white;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
nav header {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
nav input {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
nav header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
;(function() {
|
;(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var loadButton = document.getElementById('load-button');
|
var schemaUrlForm = document.getElementById('schema-url-form');
|
||||||
var schemaUrlInput = document.getElementById('schema-url-input');
|
var schemaUrlInput = document.getElementById('schema-url-input');
|
||||||
loadButton.addEventListener('click', function() {
|
schemaUrlForm.addEventListener('submit', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
Redoc.init(schemaUrlInput.value);
|
Redoc.init(schemaUrlInput.value);
|
||||||
|
return false;
|
||||||
})
|
})
|
||||||
})();
|
})();
|
||||||
|
|
5413
demo/rebilly.json
5413
demo/rebilly.json
File diff suppressed because it is too large
Load Diff
|
@ -29,7 +29,7 @@
|
||||||
},{
|
},{
|
||||||
"name": "JSONP",
|
"name": "JSONP",
|
||||||
"x-traitTag": true,
|
"x-traitTag": true,
|
||||||
"description": "If you're writing an AJAX application, and you'd like to wrap our response with a callback, all you have to do is specify a callback parameter with any API call:\n```\n https://api.instagram.com/v1/tags/coffee/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&callback=callbackFunction\n```\nWould respond with:\n```js\ncallbackFunction({\n ...\n});\n```",
|
"description": "If you're writing an AJAX application, and you'd like to wrap our response with a callback, all you have to do is specify a callback parameter with any API call:\n```\n https://api.instagram.com/v1/tags/coffee/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&callback=callbackFunction\n```\nWould respond with:\n```js\ncallbackFunction({\n ...\n});\n``` \n > Example of markdown blockquote",
|
||||||
"externalDocs": {
|
"externalDocs": {
|
||||||
"description": "Find out more",
|
"description": "Find out more",
|
||||||
"url": "http://swagger.io"
|
"url": "http://swagger.io"
|
||||||
|
|
|
@ -1,34 +1,26 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {Component, View, OnInit, OnDestroy, ElementRef} from 'angular2/core';
|
import {Directive, ElementRef} from 'angular2/core';
|
||||||
import {BrowserDomAdapter} from 'angular2/platform/browser';
|
import {BrowserDomAdapter} from 'angular2/platform/browser';
|
||||||
|
|
||||||
@Component({
|
@Directive({
|
||||||
selector: 'sticky-sidebar',
|
selector: '[sticky-sidebar]',
|
||||||
inputs: ['scrollParent', 'scrollYOffset']
|
inputs: ['scrollParent', 'scrollYOffset']
|
||||||
})
|
})
|
||||||
@View({
|
|
||||||
template: `
|
|
||||||
<div class="sticky-sidebar">
|
|
||||||
<ng-content></ng-content>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
lifecycle: [OnInit, OnDestroy]
|
|
||||||
})
|
|
||||||
export default class StickySidebar {
|
export default class StickySidebar {
|
||||||
constructor(elementRef, adapter) {
|
constructor(elementRef, dom) {
|
||||||
this.element = elementRef.nativeElement;
|
this.element = elementRef.nativeElement;
|
||||||
this.adapter = adapter;
|
this.dom = dom;
|
||||||
|
|
||||||
// initial styling
|
// initial styling
|
||||||
this.adapter.setStyle(this.element, 'position', 'absolute');
|
this.dom.setStyle(this.element, 'position', 'absolute');
|
||||||
this.adapter.setStyle(this.element, 'top', '0');
|
this.dom.setStyle(this.element, 'top', '0');
|
||||||
this.adapter.setStyle(this.element, 'bottom', '0');
|
this.dom.setStyle(this.element, 'bottom', '0');
|
||||||
this.adapter.setStyle(this.element, 'max-height', '100%');
|
this.dom.setStyle(this.element, 'max-height', '100%');
|
||||||
}
|
}
|
||||||
|
|
||||||
bind() {
|
bind() {
|
||||||
this.cancelScrollBinding = this.adapter.onAndCancel(this.scrollParent, 'scroll', () => { this.updatePosition(); });
|
this.cancelScrollBinding = this.dom.onAndCancel(this.scrollParent, 'scroll', () => { this.updatePosition(); });
|
||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,13 +37,13 @@ export default class StickySidebar {
|
||||||
}
|
}
|
||||||
|
|
||||||
stick() {
|
stick() {
|
||||||
this.adapter.setStyle(this.element, 'position', 'fixed');
|
this.dom.setStyle(this.element, 'position', 'fixed');
|
||||||
this.adapter.setStyle(this.element, 'top', this.scrollYOffset() + 'px');
|
this.dom.setStyle(this.element, 'top', this.scrollYOffset() + 'px');
|
||||||
}
|
}
|
||||||
|
|
||||||
unstick() {
|
unstick() {
|
||||||
this.adapter.setStyle(this.element, 'position', 'absolute');
|
this.dom.setStyle(this.element, 'position', 'absolute');
|
||||||
this.adapter.setStyle(this.element, 'top', 0);
|
this.dom.setStyle(this.element, 'top', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
get scrollY() {
|
get scrollY() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { getChildDebugElement } from 'tests/helpers';
|
import { getChildDebugElementByType } from 'tests/helpers';
|
||||||
import {Component, View, provide} from 'angular2/core';
|
import {Component, View, provide} from 'angular2/core';
|
||||||
import {BrowserDomAdapter} from 'angular2/platform/browser';
|
import {BrowserDomAdapter} from 'angular2/platform/browser';
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ describe('Common components', () => {
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
builder.createAsync(TestApp).then(_fixture => {
|
builder.createAsync(TestApp).then(_fixture => {
|
||||||
fixture = _fixture;
|
fixture = _fixture;
|
||||||
let debugEl = getChildDebugElement(fixture.debugElement, 'sticky-sidebar');
|
let debugEl = getChildDebugElementByType(fixture.debugElement, StickySidebar);
|
||||||
component = debugEl.componentInstance;
|
component = debugEl.componentInstance;
|
||||||
done();
|
done();
|
||||||
}, err => done.fail(err));
|
}, err => done.fail(err));
|
||||||
|
@ -67,8 +67,8 @@ describe('Common components', () => {
|
||||||
`<div style="padding-top: 20px">
|
`<div style="padding-top: 20px">
|
||||||
<div style="height: 20px; position: fixed; top: 0;"> </div>
|
<div style="height: 20px; position: fixed; top: 0;"> </div>
|
||||||
<div style="position: relative">
|
<div style="position: relative">
|
||||||
<sticky-sidebar [scrollParent]="scrollParent" [scrollYOffset]="options.scrollYOffset">
|
<div sticky-sidebar [scrollParent]="scrollParent" [scrollYOffset]="options.scrollYOffset">
|
||||||
</sticky-sidebar>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,8 @@ describe('Common components', () => {
|
||||||
let tabs = childDebugEls.map(debugEl => debugEl.componentInstance);
|
let tabs = childDebugEls.map(debugEl => debugEl.componentInstance);
|
||||||
let [tab1, tab2] = tabs;
|
let [tab1, tab2] = tabs;
|
||||||
|
|
||||||
tab1.active.should.be.true;
|
tab1.active.should.be.true();
|
||||||
tab2.active.should.be.false;
|
tab2.active.should.be.false();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should change active tab on click', () => {
|
it('should change active tab on click', () => {
|
||||||
|
|
|
@ -6,7 +6,7 @@ $side-bar-bg-color: #FAFAFA;
|
||||||
|
|
||||||
$side-menu-item-color: #384248;
|
$side-menu-item-color: #384248;
|
||||||
$side-menu-even-bg-color: #F0F0F0;
|
$side-menu-even-bg-color: #F0F0F0;
|
||||||
$side-menu-active-bg-color: #E6E6E6;
|
$side-menu-active-bg-color: #DEDEDE;
|
||||||
$side-menu-item-hpadding: 20px;
|
$side-menu-item-hpadding: 20px;
|
||||||
$side-menu-item-vpadding: 5px;
|
$side-menu-item-vpadding: 5px;
|
||||||
|
|
||||||
|
@ -16,3 +16,5 @@ $sample-panel-headers-color: #8A9094;
|
||||||
$sample-panel-color: #CFD2D3;
|
$sample-panel-color: #CFD2D3;
|
||||||
|
|
||||||
$tree-lines-color: #7D97CE;
|
$tree-lines-color: #7D97CE;
|
||||||
|
|
||||||
|
$side-menu-mobile-breakpoint: 1000px;
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
|
|
||||||
.api-info-header {
|
.api-info-header {
|
||||||
color: $headers-color;
|
color: $headers-color;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
160
lib/components/JsonSchema/json-schema-common.scss
Normal file
160
lib/components/JsonSchema/json-schema-common.scss
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
@import '../../common/styles/variables';
|
||||||
|
$lines-width: 1px;
|
||||||
|
$bullet-size: 7px;
|
||||||
|
$cell-spacing: 25px;
|
||||||
|
$cell-padding: 10px;
|
||||||
|
$bullet-margin: 10px;
|
||||||
|
$line-border: $lines-width solid $tree-lines-color;
|
||||||
|
$line-border-erase: ($lines-width + 1px) solid white;
|
||||||
|
|
||||||
|
$param-name-height: 20px;
|
||||||
|
|
||||||
|
$sub-schema-offset: ($bullet-size/2) + $bullet-margin;
|
||||||
|
|
||||||
|
.param-schema {
|
||||||
|
padding-left: $sub-schema-offset - $lines-width;
|
||||||
|
border-left: $line-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-wrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-schema:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: $sub-schema-offset;
|
||||||
|
top: ($param-name-height/2) + $cell-padding;
|
||||||
|
bottom: 0;
|
||||||
|
border-left: $line-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-name {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: $cell-padding $cell-spacing $cell-padding 0;
|
||||||
|
font-weight: bold;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: $param-name-height;
|
||||||
|
border-left: $line-border;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-info {
|
||||||
|
width: 100%;
|
||||||
|
padding: $cell-padding 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-required {
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: $param-name-height;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-type {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: $param-name-height;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-type.array:before {
|
||||||
|
content: "Array of ";
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-type.string {
|
||||||
|
color: rgba(0, 80, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-type.integer, .param-type.number {
|
||||||
|
color: rgba(74, 139, 179, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-type.object {
|
||||||
|
color: rgba(0, 50, 159, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-type.boolean {
|
||||||
|
color: firebrick;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-type.with-hint {
|
||||||
|
&:before, &:after {
|
||||||
|
content: "\\00a0";
|
||||||
|
}
|
||||||
|
background-color: rgba(0, 50, 159, 0.1);
|
||||||
|
padding: 0.2em 0;
|
||||||
|
font-size: 0.85em;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-type-trivial {
|
||||||
|
margin: 10px 10px 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tree */
|
||||||
|
|
||||||
|
// Bullet
|
||||||
|
.param-name > span:before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: $bullet-size;
|
||||||
|
height: $bullet-size;
|
||||||
|
background-color: $tree-lines-color;
|
||||||
|
margin: 0 $bullet-margin;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-name > span:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
border-top: $line-border;
|
||||||
|
width: $bullet-margin;
|
||||||
|
left: 0;
|
||||||
|
top: ($param-name-height/2) + $cell-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-wrap:first-of-type > .param > .param-name:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: -$lines-width;
|
||||||
|
top: 0;
|
||||||
|
border-left: $line-border-erase;
|
||||||
|
height: ($param-name-height/2) + $cell-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-wrap:last-of-type > .param > .param-name {
|
||||||
|
position: static;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: -$lines-width;
|
||||||
|
border-left: $line-border-erase;
|
||||||
|
top: ($param-name-height/2) + $cell-padding + $lines-width;
|
||||||
|
background-color: white;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-wrap:last-of-type > .param-schema {
|
||||||
|
border-left-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-schema .param-wrap:first-of-type .param-name:before {
|
||||||
|
display: none !important;
|
||||||
|
}
|
35
lib/components/JsonSchema/json-schema-lazy.js
Normal file
35
lib/components/JsonSchema/json-schema-lazy.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import {Component, View, ElementRef} from 'angular2/core';
|
||||||
|
import {CORE_DIRECTIVES} from 'angular2/common';
|
||||||
|
import JsonSchema from './json-schema';
|
||||||
|
import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader';
|
||||||
|
import OptionsManager from '../../options';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'json-schema-lazy',
|
||||||
|
inputs: ['pointer']
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
template: '',
|
||||||
|
directives: [CORE_DIRECTIVES]
|
||||||
|
})
|
||||||
|
export default class JsonSchemaLazy {
|
||||||
|
|
||||||
|
constructor(elementRef, dcl) {
|
||||||
|
this.elementRef = elementRef;
|
||||||
|
this.dcl = dcl;
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
if (OptionsManager.instance().options.disableLazySchemas) return;
|
||||||
|
if (this.loaded) return;
|
||||||
|
if (this.pointer) {
|
||||||
|
this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => {
|
||||||
|
compRef.instance.pointer = this.pointer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.loaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JsonSchemaLazy.parameters = [[ElementRef], [DynamicComponentLoader]];
|
87
lib/components/JsonSchema/json-schema-lazy.spec.js
Normal file
87
lib/components/JsonSchema/json-schema-lazy.spec.js
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { getChildDebugElement } from 'tests/helpers';
|
||||||
|
import {Component, View, provide} from 'angular2/core';
|
||||||
|
import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TestComponentBuilder,
|
||||||
|
inject,
|
||||||
|
beforeEach,
|
||||||
|
beforeEachProviders,
|
||||||
|
it
|
||||||
|
} from 'angular2/testing';
|
||||||
|
|
||||||
|
import JsonSchemaLazy from 'lib/components/JsonSchema/json-schema-lazy';
|
||||||
|
import SchemaManager from 'lib/utils/SchemaManager';
|
||||||
|
|
||||||
|
describe('Redoc components', () => {
|
||||||
|
describe('JsonSchemaLazy Component', () => {
|
||||||
|
let builder;
|
||||||
|
let component;
|
||||||
|
let schemaMgr = new SchemaManager();
|
||||||
|
let fixture;
|
||||||
|
let loader;
|
||||||
|
let appRef = {
|
||||||
|
instance: {}
|
||||||
|
};
|
||||||
|
beforeEachProviders(() => [
|
||||||
|
provide(SchemaManager, {useValue: schemaMgr})
|
||||||
|
]);
|
||||||
|
beforeEach(inject([TestComponentBuilder, DynamicComponentLoader], (tcb, dcl) => {
|
||||||
|
builder = tcb;
|
||||||
|
loader = dcl;
|
||||||
|
spyOn(loader, 'loadNextToLocation').and.returnValue({then: (fn) => fn(appRef)});
|
||||||
|
}));
|
||||||
|
beforeEach((done) => {
|
||||||
|
builder.createAsync(TestApp).then(_fixture => {
|
||||||
|
fixture = _fixture;
|
||||||
|
let debugEl = getChildDebugElement(fixture.debugElement, 'json-schema-lazy');
|
||||||
|
component = debugEl.componentInstance;
|
||||||
|
done();
|
||||||
|
}, err => done.fail(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
loader.loadNextToLocation.and.callThrough();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should init component', () => {
|
||||||
|
expect(component).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should run loadNextToLocation on load', () => {
|
||||||
|
component.pointer = '#/def';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.load();
|
||||||
|
expect(loader.loadNextToLocation).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not run loadNextToLocation if already loaded', () => {
|
||||||
|
component.pointer = '#/def';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.load();
|
||||||
|
component.load();
|
||||||
|
expect(loader.loadNextToLocation.calls.count()).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should init json-schema with correct pointer', () => {
|
||||||
|
component.pointer = '#/def';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.load();
|
||||||
|
expect(appRef.instance.pointer).toEqual(component.pointer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/** Test component that contains a Method. */
|
||||||
|
@Component({selector: 'test-app'})
|
||||||
|
@View({
|
||||||
|
directives: [JsonSchemaLazy],
|
||||||
|
providers: [SchemaManager, DynamicComponentLoader],
|
||||||
|
template:
|
||||||
|
`<json-schema-lazy></json-schema-lazy>`
|
||||||
|
})
|
||||||
|
class TestApp {
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
<span *ngIf="isTrivial" class="param-type param-type-trivial" [ngClass]="type">{{_displayType}}</span>
|
<span *ngIf="isTrivial" class="param-wrap">
|
||||||
|
<span class="param-type param-type-trivial {{type}}"
|
||||||
|
[ngClass]="{'with-hint': _displayTypeHint}" title="{{_displayTypeHint}}">{{_displayType}}</span>
|
||||||
|
</span>
|
||||||
<div *ngIf="!isTrivial" class="params-wrap" [ngClass]="{'params-array': isArray}">
|
<div *ngIf="!isTrivial" class="params-wrap" [ngClass]="{'params-array': isArray}">
|
||||||
<div *ngFor="#prop of data.properties" class="param-wrap">
|
<div *ngFor="#prop of data.properties" class="param-wrap">
|
||||||
<div class="param" [ngClass]="{'discriminator': prop.isDiscriminator}">
|
<div class="param" [ngClass]="{'discriminator': prop.isDiscriminator}">
|
||||||
|
@ -7,7 +10,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="param-info">
|
<div class="param-info">
|
||||||
<div>
|
<div>
|
||||||
<span class="param-type" [ngClass]="prop.type">{{prop._displayType}} {{prop._displayFormat}}</span>
|
<span class="param-type {{prop.type}}" [ngClass]="{'with-hint': prop._displayTypeHint}"
|
||||||
|
title="{{prop._displayTypeHint}}"> {{prop._displayType}} {{prop._displayFormat}}</span>
|
||||||
<span *ngIf="prop.isRequired" class="param-required">Required</span>
|
<span *ngIf="prop.isRequired" class="param-required">Required</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-description" innerHtml="{{prop.description | marked}}"></div>
|
<div class="param-description" innerHtml="{{prop.description | marked}}"></div>
|
||||||
|
|
|
@ -63,14 +63,15 @@ export default class JsonSchema extends BaseComponent {
|
||||||
|
|
||||||
if (!schema.properties) {
|
if (!schema.properties) {
|
||||||
this.isTrivial = true;
|
this.isTrivial = true;
|
||||||
this._displayType = `${schema.type} (Custom key-value pairs)`;
|
this._displayType = schema.type;
|
||||||
|
this._displayTypeHint = 'This field may contain data of any type';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let discriminatorFieldIdx = -1;
|
let discriminatorFieldIdx = -1;
|
||||||
let props = Object.keys(schema.properties).map((prop, idx) => {
|
let props = Object.keys(schema.properties).map((prop, idx) => {
|
||||||
let propData = schema.properties[prop];
|
let propData = schema.properties[prop];
|
||||||
this.injectPropData(prop, propData, schema);
|
propData = this.injectPropData(prop, propData, schema);
|
||||||
if (propData.isDiscriminator) discriminatorFieldIdx = idx;
|
if (propData.isDiscriminator) discriminatorFieldIdx = idx;
|
||||||
return propData;
|
return propData;
|
||||||
});
|
});
|
||||||
|
@ -97,6 +98,7 @@ export default class JsonSchema extends BaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
injectPropData(prop, propData, schema) {
|
injectPropData(prop, propData, schema) {
|
||||||
|
propData = Object.assign({}, propData);
|
||||||
propData._name = prop;
|
propData._name = prop;
|
||||||
propData.isRequired = this.requiredMap[prop];
|
propData.isRequired = this.requiredMap[prop];
|
||||||
propData._displayType = propData.type;
|
propData._displayType = propData.type;
|
||||||
|
@ -108,16 +110,24 @@ export default class JsonSchema extends BaseComponent {
|
||||||
itemType = propData.items.title || 'object';
|
itemType = propData.items.title || 'object';
|
||||||
propData._pointer = propData.items._pointer || JsonPointer.join(this.pointer, ['properties', prop, 'items']);
|
propData._pointer = propData.items._pointer || JsonPointer.join(this.pointer, ['properties', prop, 'items']);
|
||||||
}
|
}
|
||||||
propData._displayType = `array of ${itemType}`;
|
propData._displayType = `${itemType}`;
|
||||||
propData.format = itemFormat;
|
propData.format = itemFormat;
|
||||||
propData._isArray = true;
|
propData._isArray = true;
|
||||||
|
propData.type = 'array ' + propData.items.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propData.type === 'object') {
|
if (propData.type === 'object') {
|
||||||
propData._displayType = propData.title || 'object';
|
propData._displayType = propData.title || 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!propData.type) {
|
||||||
|
propData._displayType = '< * >';
|
||||||
|
propData._displayTypeHint = 'This field may contain data of any type';
|
||||||
|
}
|
||||||
|
|
||||||
if (propData.format) propData._displayFormat = `<${propData.format}>`;
|
if (propData.format) propData._displayFormat = `<${propData.format}>`;
|
||||||
|
|
||||||
|
return propData;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
|
@ -1,129 +1,4 @@
|
||||||
@import '../../common/styles/variables';
|
@import 'json-schema-common';
|
||||||
$lines-width: 1px;
|
|
||||||
$bullet-size: 7px;
|
|
||||||
$cell-spacing: 25px;
|
|
||||||
$cell-padding: 10px;
|
|
||||||
$bullet-margin: 10px;
|
|
||||||
$line-border: $lines-width solid $tree-lines-color;
|
|
||||||
$line-border-erase: ($lines-width + 1px) solid white;
|
|
||||||
|
|
||||||
$param-name-height: 20px;
|
|
||||||
|
|
||||||
$sub-schema-offset: ($bullet-size/2) + $bullet-margin;
|
|
||||||
|
|
||||||
.param-schema {
|
|
||||||
padding-left: $sub-schema-offset - $lines-width;
|
|
||||||
border-left: $line-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-schema:before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: $sub-schema-offset;
|
|
||||||
top: ($param-name-height/2) + $cell-padding;
|
|
||||||
bottom: 0;
|
|
||||||
border-left: $line-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-name {
|
|
||||||
font-size: 14px;
|
|
||||||
padding: $cell-padding $cell-spacing $cell-padding 0;
|
|
||||||
font-weight: bold;
|
|
||||||
box-sizing: border-box;
|
|
||||||
line-height: $param-name-height;
|
|
||||||
border-left: $line-border;
|
|
||||||
white-space: nowrap;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-info {
|
|
||||||
width: 100%;
|
|
||||||
padding: $cell-padding 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-required {
|
|
||||||
color: red;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: $param-name-height;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-type {
|
|
||||||
text-transform: capitalize;
|
|
||||||
color: #999;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: $param-name-height;
|
|
||||||
vertical-align: middle;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-type-trivial {
|
|
||||||
margin: 10px 10px 0;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tree */
|
|
||||||
|
|
||||||
// Bullet
|
|
||||||
.param-name > span:before {
|
|
||||||
content: "";
|
|
||||||
display: inline-block;
|
|
||||||
width: $bullet-size;
|
|
||||||
height: $bullet-size;
|
|
||||||
background-color: $tree-lines-color;
|
|
||||||
margin: 0 $bullet-margin;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-name > span:after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
border-top: $line-border;
|
|
||||||
width: $bullet-margin;
|
|
||||||
left: 0;
|
|
||||||
top: ($param-name-height/2) + $cell-padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-wrap:first-of-type .param-name:before {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
left: -$lines-width;
|
|
||||||
top: 0;
|
|
||||||
border-left: $line-border-erase;
|
|
||||||
height: ($param-name-height/2) + $cell-padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-wrap:last-of-type > .param > .param-name:after {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
left: -$lines-width - 1px;
|
|
||||||
border-left: $line-border-erase;
|
|
||||||
top: ($param-name-height/2) + $cell-padding + $lines-width;
|
|
||||||
background-color: white;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-wrap:last-of-type > .param-schema {
|
|
||||||
border-left-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-schema .param-wrap:first-of-type .param-name:before {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* styles for array-schema for array */
|
/* styles for array-schema for array */
|
||||||
$array-marker-font-sz: 12px;
|
$array-marker-font-sz: 12px;
|
||||||
|
@ -166,6 +41,10 @@ $array-marker-line-height: 1.5;
|
||||||
height: ($param-name-height/2) + $cell-padding;
|
height: ($param-name-height/2) + $cell-padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.params-wrap > .param > .param-schema.param-array {
|
||||||
|
border-left-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.param.discriminator {
|
.param.discriminator {
|
||||||
> div {
|
> div {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
|
|
73
lib/components/JsonSchema/json-schema.spec.js
Normal file
73
lib/components/JsonSchema/json-schema.spec.js
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { getChildDebugElement } from 'tests/helpers';
|
||||||
|
import {Component, View, provide} from 'angular2/core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TestComponentBuilder,
|
||||||
|
inject,
|
||||||
|
beforeEach,
|
||||||
|
beforeEachProviders,
|
||||||
|
it
|
||||||
|
} from 'angular2/testing';
|
||||||
|
|
||||||
|
import JsonSchema from 'lib/components/JsonSchema/json-schema';
|
||||||
|
import SchemaManager from 'lib/utils/SchemaManager';
|
||||||
|
|
||||||
|
describe('Redoc components', () => {
|
||||||
|
describe('JsonSchema Component', () => {
|
||||||
|
let builder;
|
||||||
|
let component;
|
||||||
|
let schemaMgr = new SchemaManager();
|
||||||
|
let fixture;
|
||||||
|
beforeEachProviders(() => [
|
||||||
|
provide(SchemaManager, {useValue: schemaMgr})
|
||||||
|
]);
|
||||||
|
beforeEach(inject([TestComponentBuilder], (tcb) => {
|
||||||
|
builder = tcb;
|
||||||
|
}));
|
||||||
|
beforeEach((done) => {
|
||||||
|
builder.createAsync(TestApp).then(_fixture => {
|
||||||
|
fixture = _fixture;
|
||||||
|
let debugEl = getChildDebugElement(fixture.debugElement, 'json-schema');
|
||||||
|
component = debugEl.componentInstance;
|
||||||
|
done();
|
||||||
|
}, err => done.fail(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should init component', () => {
|
||||||
|
component.pointer = '';
|
||||||
|
schemaMgr._schema = {type: 'object'};
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set isTrivial for non-object/array types', () => {
|
||||||
|
component.pointer = '';
|
||||||
|
schemaMgr._schema = {type: 'string'};
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.isTrivial.should.be.true();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use < * > notation for prop without type', () => {
|
||||||
|
component.pointer = '';
|
||||||
|
schemaMgr._schema = {type: 'object', properties: {
|
||||||
|
test: {}
|
||||||
|
}};
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.data.properties[0]._displayType.should.be.equal('< * >');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/** Test component that contains a Method. */
|
||||||
|
@Component({selector: 'test-app'})
|
||||||
|
@View({
|
||||||
|
directives: [JsonSchema],
|
||||||
|
providers: [SchemaManager],
|
||||||
|
template:
|
||||||
|
`<json-schema></json-schema>`
|
||||||
|
})
|
||||||
|
class TestApp {
|
||||||
|
}
|
|
@ -103,6 +103,7 @@ responses-samples {
|
||||||
|
|
||||||
.method-description {
|
.method-description {
|
||||||
padding: 30px 0;
|
padding: 30px 0;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.http-method {
|
.http-method {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
.tag-info h1 {
|
.tag-info h1 {
|
||||||
color: $headers-color;
|
color: $headers-color;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
font-weight: bold;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.methods {
|
.methods {
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
padding: 0.2em 0;
|
padding: 0.2em 0;
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
color: #253137;
|
color: #253137;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@import '../JsonSchema/json-schema.scss';
|
@import '../JsonSchema/json-schema-common';
|
||||||
|
|
||||||
|
|
||||||
// paramters can't be multilevel so table representation works for it without javascript
|
// paramters can't be multilevel so table representation works for it without javascript
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<div class="redoc-wrap">
|
<div class="redoc-wrap">
|
||||||
<sticky-sidebar [scrollParent]="scrollParent" [scrollYOffset]="options.scrollYOffset">
|
<div class="menu-content" sticky-sidebar [scrollParent]="scrollParent" [scrollYOffset]="options.scrollYOffset">
|
||||||
<api-logo> </api-logo>
|
<api-logo> </api-logo>
|
||||||
<side-menu> </side-menu>
|
<side-menu> </side-menu>
|
||||||
</sticky-sidebar>
|
</div>
|
||||||
<div id="api-content">
|
<div id="api-content">
|
||||||
<api-info> </api-info>
|
<api-info> </api-info>
|
||||||
<methods-list> </methods-list>
|
<methods-list> </methods-list>
|
||||||
|
|
|
@ -16,9 +16,11 @@ import {ElementRef} from 'angular2/core';
|
||||||
import {BrowserDomAdapter, bootstrap} from 'angular2/platform/browser';
|
import {BrowserDomAdapter, bootstrap} from 'angular2/platform/browser';
|
||||||
import detectScollParent from 'scrollparent';
|
import detectScollParent from 'scrollparent';
|
||||||
|
|
||||||
import {isFunction} from 'angular2/src/facade/lang';
|
import {isFunction, isString} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
let optionNames = new Set(['scrollYOffset']);
|
let optionNames = new Set(['scrollYOffset', 'disableLazySchemas']);
|
||||||
|
|
||||||
|
let dom = new BrowserDomAdapter();
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
selector: 'redoc',
|
selector: 'redoc',
|
||||||
|
@ -41,6 +43,7 @@ export default class Redoc extends BaseComponent {
|
||||||
this.parseOptions();
|
this.parseOptions();
|
||||||
this.options = Object.assign({}, optionsMgr.options, this.options);
|
this.options = Object.assign({}, optionsMgr.options, this.options);
|
||||||
this.normalizeOptions();
|
this.normalizeOptions();
|
||||||
|
optionsMgr.options = this.options;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseOptions() {
|
parseOptions() {
|
||||||
|
@ -79,12 +82,63 @@ export default class Redoc extends BaseComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isString(this.options.disableLazySchemas)) this.options.disableLazySchemas = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static showLoadingAnimation() {
|
||||||
|
if (!dom.query('#redoc-loading-style')) {
|
||||||
|
let animStyle = dom.createStyleElement(`
|
||||||
|
redoc.loading {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
min-height:350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
redoc.loading:before {
|
||||||
|
content: "Loading...";
|
||||||
|
font-size: 28px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
color: #3F5C9C;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 9999;
|
||||||
|
opacity: 1;
|
||||||
|
transition: all 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
redoc.loading-remove:before {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
animStyle.id = 'redoc-loading-style';
|
||||||
|
dom.appendChild(dom.defaultDoc().head, animStyle);
|
||||||
|
}
|
||||||
|
let elem = dom.query('redoc');
|
||||||
|
dom.addClass(elem, 'loading');
|
||||||
|
}
|
||||||
|
|
||||||
|
static hideLoadingAnimation() {
|
||||||
|
let redocEl = dom.query('redoc');
|
||||||
|
dom.addClass(redocEl, 'loading-remove');
|
||||||
|
setTimeout(() => {
|
||||||
|
dom.removeClass(redocEl, 'loading-remove');
|
||||||
|
dom.removeClass(redocEl, 'loading');
|
||||||
|
}, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
static init(schemaUrl, options) {
|
static init(schemaUrl, options) {
|
||||||
if (Redoc.appRef) {
|
if (Redoc.appRef) {
|
||||||
Redoc.dispose();
|
Redoc.dispose();
|
||||||
}
|
}
|
||||||
|
Redoc.showLoadingAnimation();
|
||||||
return SchemaManager.instance().load(schemaUrl)
|
return SchemaManager.instance().load(schemaUrl)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
(new OptionsManager()).options = options;
|
(new OptionsManager()).options = options;
|
||||||
|
@ -92,6 +146,7 @@ export default class Redoc extends BaseComponent {
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(appRef) => {
|
(appRef) => {
|
||||||
|
Redoc.hideLoadingAnimation();
|
||||||
Redoc.appRef = appRef;
|
Redoc.appRef = appRef;
|
||||||
redocEvents.bootstrapped.next();
|
redocEvents.bootstrapped.next();
|
||||||
console.log('ReDoc bootstrapped!');
|
console.log('ReDoc bootstrapped!');
|
||||||
|
@ -117,6 +172,7 @@ export default class Redoc extends BaseComponent {
|
||||||
static dispose() {
|
static dispose() {
|
||||||
let dom = new BrowserDomAdapter();
|
let dom = new BrowserDomAdapter();
|
||||||
let el = dom.query('redoc');
|
let el = dom.query('redoc');
|
||||||
|
let elClone;
|
||||||
let parent;
|
let parent;
|
||||||
let nextSibling;
|
let nextSibling;
|
||||||
if (el) {
|
if (el) {
|
||||||
|
@ -124,14 +180,15 @@ export default class Redoc extends BaseComponent {
|
||||||
nextSibling = el.nextElementSibling;
|
nextSibling = el.nextElementSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
elClone = el.cloneNode(false);
|
||||||
|
|
||||||
if (Redoc.appRef) {
|
if (Redoc.appRef) {
|
||||||
Redoc.appRef.dispose();
|
Redoc.appRef.dispose();
|
||||||
Redoc.appRef = null;
|
Redoc.appRef = null;
|
||||||
|
|
||||||
// Redoc dispose removes host element, so need to restore it
|
// Redoc dispose removes host element, so need to restore it
|
||||||
el = dom.createElement('redoc');
|
elClone.innerHTML = 'Loading...';
|
||||||
el.innerText = 'Loading...';
|
parent && parent.insertBefore(elClone, nextSibling);
|
||||||
parent && parent.insertBefore(el, nextSibling);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,24 +5,6 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host {
|
|
||||||
pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
padding: 10px;
|
|
||||||
overflow-x: auto;
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.redoc-wrap {
|
.redoc-wrap {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -49,17 +31,48 @@ api-info {
|
||||||
api-logo {
|
api-logo {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sticky-sidebar {
|
[sticky-sidebar] {
|
||||||
width: $side-bar-width;
|
width: $side-bar-width;
|
||||||
|
background-color: $side-bar-bg-color;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
background-color: $side-bar-bg-color;
|
|
||||||
|
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
bottom: auto !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#api-content {
|
#api-content {
|
||||||
margin-left: $side-bar-width;
|
margin-left: $side-bar-width;
|
||||||
|
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||||
|
padding-top: 3em;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#api-content:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;;
|
||||||
|
left:0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: black;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#api-content.menu-opened:after {
|
||||||
|
display: block;;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
|
@ -77,3 +90,104 @@ footer {
|
||||||
color: $headers-color;
|
color: $headers-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* global redoc styles */
|
||||||
|
|
||||||
|
:host p {
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* markdown elements */
|
||||||
|
|
||||||
|
:host .redoc-markdown-block {
|
||||||
|
pre {
|
||||||
|
font-family: Courier, monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
background-color: rgba(0,0,0,0.04);
|
||||||
|
padding: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
line-height: normal;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&:before, &:after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: Courier, monospace;
|
||||||
|
background-color: rgba(0,0,0,0.04);
|
||||||
|
padding: 0.2em 0;
|
||||||
|
font-size: 0.85em;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&:before, &:after {
|
||||||
|
letter-spacing: -0.2em;
|
||||||
|
content: "\\00a0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
padding: 0 15px;
|
||||||
|
color: #777;
|
||||||
|
border-left: 4px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
padding-left: 2em;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
word-break: normal;
|
||||||
|
word-break: keep-all;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr {
|
||||||
|
background-color: #fff;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
|
||||||
|
&:nth-child(2n) {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table th, table td {
|
||||||
|
padding: 6px 13px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -105,28 +105,39 @@ describe('Redoc components', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('Redoc init', () => {
|
describe('Redoc init', () => {
|
||||||
it('should return promise', () => {
|
let dom = new BrowserDomAdapter();
|
||||||
let res = Redoc.init();
|
let elem;
|
||||||
res.should.be.instanceof(Promise);
|
beforeEach(() => {
|
||||||
});
|
elem = dom.createElement('redoc');
|
||||||
|
dom.defaultDoc().body.appendChild(elem);
|
||||||
it('should reject promise for not specifed url', (done) => {
|
|
||||||
let res = Redoc.init();
|
|
||||||
res.then(() => { done.fail('Should not been called'); }, () => {
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
//skip because of PhantomJS crashes on this testcase
|
afterEach(() => {
|
||||||
xit('should init redoc', (done) => {
|
dom.defaultDoc().body.removeChild(elem);
|
||||||
var node = document.createElement('redoc');
|
});
|
||||||
document.body.appendChild(node);
|
|
||||||
let res = Redoc.init('/tests/schemas/extended-petstore.json');
|
it('should return promise', () => {
|
||||||
res.then(() => { done(); }, () => {
|
let res = Redoc.init();
|
||||||
done.fail('Error handler should not been called');
|
res.should.be.instanceof(Promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject promise for not specifed url', (done) => {
|
||||||
|
let res = Redoc.init();
|
||||||
|
res.then(() => { done.fail('Should not been called'); }, () => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//skip because of PhantomJS crashes on this testcase
|
||||||
|
xit('should init redoc', (done) => {
|
||||||
|
var node = document.createElement('redoc');
|
||||||
|
document.body.appendChild(node);
|
||||||
|
let res = Redoc.init('/tests/schemas/extended-petstore.json');
|
||||||
|
res.then(() => { done(); }, () => {
|
||||||
|
done.fail('Error handler should not been called');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -217,7 +228,7 @@ describe('Redoc init', () => {
|
||||||
@View({
|
@View({
|
||||||
directives: [Redoc],
|
directives: [Redoc],
|
||||||
template:
|
template:
|
||||||
`<redoc></redoc>`
|
`<redoc disable-lazy-schemas></redoc>`
|
||||||
})
|
})
|
||||||
class TestApp {
|
class TestApp {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<h2 class="responses-list-header" *ngIf="data.responses.length"> Responses </h2>
|
<h2 class="responses-list-header" *ngIf="data.responses.length"> Responses </h2>
|
||||||
<zippy *ngFor="#response of data.responses" title="{{response.code}} {{response.description}}"
|
<zippy *ngFor="#response of data.responses" title="{{response.code}} {{response.description}}"
|
||||||
[type]="response.type" [empty]="!response.schema">
|
[type]="response.type" [empty]="response.empty" (open)="lazySchema.load()">
|
||||||
<div *ngIf="response.headers" class="response-headers">
|
<div *ngIf="response.headers" class="response-headers">
|
||||||
<header>
|
<header>
|
||||||
Headers
|
Headers
|
||||||
|
@ -14,6 +14,8 @@
|
||||||
<header>
|
<header>
|
||||||
Response schema
|
Response schema
|
||||||
</header>
|
</header>
|
||||||
<json-schema *ngIf="response.schema" class="schema type" pointer="{{response.pointer}}/schema">
|
<json-schema *ngIf="response.schema && !enabledLazy" class="schema type" pointer="{{response.pointer}}/schema">
|
||||||
</json-schema>
|
</json-schema>
|
||||||
|
<json-schema-lazy #lazySchema pointer="{{response.schema ? response.pointer + '/schema' : null}}">
|
||||||
|
</json-schema-lazy>
|
||||||
</zippy>
|
</zippy>
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
import {RedocComponent, BaseComponent} from '../base';
|
import {RedocComponent, BaseComponent} from '../base';
|
||||||
import JsonPointer from '../../utils/JsonPointer';
|
import JsonPointer from '../../utils/JsonPointer';
|
||||||
import JsonSchema from '../JsonSchema/json-schema';
|
import JsonSchema from '../JsonSchema/json-schema';
|
||||||
|
import JsonSchemaLazy from '../JsonSchema/json-schema-lazy';
|
||||||
import Zippy from '../../common/components/Zippy/zippy';
|
import Zippy from '../../common/components/Zippy/zippy';
|
||||||
import {statusCodeType} from '../../utils/helpers';
|
import {statusCodeType} from '../../utils/helpers';
|
||||||
|
import OptionsManager from '../../options';
|
||||||
|
|
||||||
function isNumeric(n) {
|
function isNumeric(n) {
|
||||||
return (!isNaN(parseFloat(n)) && isFinite(n));
|
return (!isNaN(parseFloat(n)) && isFinite(n));
|
||||||
|
@ -14,7 +16,7 @@ function isNumeric(n) {
|
||||||
selector: 'responses-list',
|
selector: 'responses-list',
|
||||||
templateUrl: './lib/components/ResponsesList/responses-list.html',
|
templateUrl: './lib/components/ResponsesList/responses-list.html',
|
||||||
styleUrls: ['./lib/components/ResponsesList/responses-list.css'],
|
styleUrls: ['./lib/components/ResponsesList/responses-list.css'],
|
||||||
directives: [JsonSchema, Zippy]
|
directives: [JsonSchema, Zippy, JsonSchemaLazy]
|
||||||
})
|
})
|
||||||
export default class ResponsesList extends BaseComponent {
|
export default class ResponsesList extends BaseComponent {
|
||||||
constructor(schemaMgr) {
|
constructor(schemaMgr) {
|
||||||
|
@ -24,6 +26,7 @@ export default class ResponsesList extends BaseComponent {
|
||||||
prepareModel() {
|
prepareModel() {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
this.data.responses = [];
|
this.data.responses = [];
|
||||||
|
this.enabledLazy = !OptionsManager.instance().options.disableLazySchemas;
|
||||||
|
|
||||||
let responses = this.componentSchema;
|
let responses = this.componentSchema;
|
||||||
if (!responses) return;
|
if (!responses) return;
|
||||||
|
@ -40,6 +43,7 @@ export default class ResponsesList extends BaseComponent {
|
||||||
resp.pointer = ref;
|
resp.pointer = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp.empty = !resp.schema;
|
||||||
resp.code = respCode;
|
resp.code = respCode;
|
||||||
resp.type = statusCodeType(resp.code);
|
resp.type = statusCodeType(resp.code);
|
||||||
if (resp.headers) {
|
if (resp.headers) {
|
||||||
|
@ -48,6 +52,7 @@ export default class ResponsesList extends BaseComponent {
|
||||||
respInfo.name = k;
|
respInfo.name = k;
|
||||||
return respInfo;
|
return respInfo;
|
||||||
});
|
});
|
||||||
|
resp.empty = false;
|
||||||
}
|
}
|
||||||
resp.extendable = resp.headers || resp.length;
|
resp.extendable = resp.headers || resp.length;
|
||||||
return resp;
|
return resp;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
padding: 0.2em 0;
|
padding: 0.2em 0;
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
color: #253137;
|
color: #253137;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-name {
|
.header-name {
|
||||||
|
|
|
@ -13,9 +13,10 @@ header {
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
color: $sample-panel-headers-color;
|
color: $sample-panel-headers-color;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host tabs li {
|
:host > tabs > ul li {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="snippet">
|
<div class="snippet">
|
||||||
<!-- in case sample is not available for some reason -->
|
<!-- in case sample is not available for some reason -->
|
||||||
<pre *ngIf="data.sample == null"> Sample unavailable </pre>
|
<pre *ngIf="data.sample == null"> Sample unavailable </pre>
|
||||||
<pre>{{data.sample | json}}</pre>
|
<pre innerHtml="{{data.sample | jsonFormatter}}"></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,19 +4,19 @@ import {RedocComponent, BaseComponent} from '../base';
|
||||||
|
|
||||||
import SchemaSampler from 'json-schema-instantiator';
|
import SchemaSampler from 'json-schema-instantiator';
|
||||||
|
|
||||||
|
import {JsonFormatter} from '../../utils/JsonFormatterPipe';
|
||||||
|
import {ElementRef} from 'angular2/core';
|
||||||
|
|
||||||
@RedocComponent({
|
@RedocComponent({
|
||||||
selector: 'schema-sample',
|
selector: 'schema-sample',
|
||||||
templateUrl: './lib/components/SchemaSample/schema-sample.html',
|
templateUrl: './lib/components/SchemaSample/schema-sample.html',
|
||||||
styles: [`
|
pipes: [JsonFormatter],
|
||||||
pre {
|
styleUrls: ['./lib/components/SchemaSample/schema-sample.css']
|
||||||
background-color: transparent;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
`]
|
|
||||||
})
|
})
|
||||||
export default class SchemaSample extends BaseComponent {
|
export default class SchemaSample extends BaseComponent {
|
||||||
constructor(schemaMgr) {
|
constructor(schemaMgr, elementRef) {
|
||||||
super(schemaMgr);
|
super(schemaMgr);
|
||||||
|
this.element = elementRef.nativeElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -44,5 +44,19 @@ export default class SchemaSample extends BaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data.sample = sample;
|
this.data.sample = sample;
|
||||||
|
|
||||||
|
|
||||||
|
this.element.addEventListener('click', (event) => {
|
||||||
|
var collapsed, target = event.target;
|
||||||
|
if (event.target.className === 'collapser') {
|
||||||
|
collapsed = target.parentNode.getElementsByClassName('collapsible')[0];
|
||||||
|
if (collapsed.parentNode.classList.contains('collapsed')) {
|
||||||
|
collapsed.parentNode.classList.remove('collapsed');
|
||||||
|
} else {
|
||||||
|
collapsed.parentNode.classList.add('collapsed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SchemaSample.parameters = SchemaSample.parameters.concat([[ElementRef]]);
|
||||||
|
|
112
lib/components/SchemaSample/schema-sample.scss
Normal file
112
lib/components/SchemaSample/schema-sample.scss
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
pre {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
:host {
|
||||||
|
.property {
|
||||||
|
//font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-null {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-boolean {
|
||||||
|
color: firebrick;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-number {
|
||||||
|
color: #4A8BB3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-string {
|
||||||
|
color: #66B16E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callback-function {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapser:after {
|
||||||
|
content: "-";
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed > .collapser:after {
|
||||||
|
content: "+";
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ellipsis:after {
|
||||||
|
content: " … ";
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible {
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoverable {
|
||||||
|
padding-top: 1px;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hovered {
|
||||||
|
background-color: rgba(235, 238, 249, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapser {
|
||||||
|
padding-right: 6px;
|
||||||
|
padding-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, .redoc-json ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px 0px 0px 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoverable {
|
||||||
|
transition: background-color .2s ease-out 0s;
|
||||||
|
-webkit-transition: background-color .2s ease-out 0s;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hovered {
|
||||||
|
transition-delay: .2s;
|
||||||
|
-webkit-transition-delay: .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
outline-style: solid;
|
||||||
|
outline-width: 1px;
|
||||||
|
outline-style: dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed>.collapsible {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ellipsis {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed>.ellipsis {
|
||||||
|
display: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapser {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
left: -1.5em;
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,22 @@
|
||||||
<h2 class="menu-header"> Api reference </h2>
|
<div class="mobile-nav" (click)="toggleMobileNav()">
|
||||||
<div *ngFor="var cat of data.menu; #idx = index" class="menu-cat">
|
<span class="menu-header"> API Reference: </span>
|
||||||
|
<span class="selected-item-info">
|
||||||
<label class="menu-cat-header" (click)="activateAndScroll(idx, -1)" [ngClass]="{active: cat.active}"> {{cat.name}}</label>
|
<span class="selected-tag"> {{activeCatCaption}} </span>
|
||||||
<ul class="menu-subitems" [ngClass]="{active: cat.active}">
|
<span class="selected-endpoint">{{activeItemCaption}}</span>
|
||||||
<li *ngFor="var method of cat.methods; var methIdx = index"
|
</span>
|
||||||
[ngClass]="{active: method.active}"
|
</div>
|
||||||
(click)="activateAndScroll(idx, methIdx)">
|
<div id="resources-nav">
|
||||||
{{method.summary}}
|
<h2 class="menu-header"> API reference </h2>
|
||||||
</li>
|
<div *ngFor="var cat of data.menu; #idx = index" class="menu-cat">
|
||||||
</ul>
|
|
||||||
|
<label class="menu-cat-header" (click)="activateAndScroll(idx, -1)" [ngClass]="{active: cat.active}"> {{cat.name}}</label>
|
||||||
|
<ul class="menu-subitems" [ngClass]="{active: cat.active}">
|
||||||
|
<li *ngFor="var method of cat.methods; var methIdx = index"
|
||||||
|
[ngClass]="{active: method.active}"
|
||||||
|
(click)="activateAndScroll(idx, methIdx)">
|
||||||
|
{{method.summary}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import {RedocComponent, BaseComponent} from '../base';
|
import {RedocComponent, BaseComponent} from '../base';
|
||||||
import {redocEvents} from '../../events';
|
import {redocEvents} from '../../events';
|
||||||
|
|
||||||
import {NgZone, ChangeDetectionStrategy} from 'angular2/core';
|
import {NgZone, ChangeDetectionStrategy, ElementRef} from 'angular2/core';
|
||||||
import {document} from 'angular2/src/facade/browser';
|
import {document} from 'angular2/src/facade/browser';
|
||||||
import {BrowserDomAdapter} from 'angular2/platform/browser';
|
import {BrowserDomAdapter} from 'angular2/platform/browser';
|
||||||
import {global} from 'angular2/src/facade/lang';
|
import {global} from 'angular2/src/facade/lang';
|
||||||
|
@ -27,13 +27,15 @@ const INVIEW_POSITION = {
|
||||||
changeDetection: ChangeDetectionStrategy.Default
|
changeDetection: ChangeDetectionStrategy.Default
|
||||||
})
|
})
|
||||||
export default class SideMenu extends BaseComponent {
|
export default class SideMenu extends BaseComponent {
|
||||||
constructor(schemaMgr, adapter, zone, redoc) {
|
constructor(schemaMgr, elementRef, adapter, zone, redoc) {
|
||||||
super(schemaMgr);
|
super(schemaMgr);
|
||||||
this.zone = zone;
|
this.zone = zone;
|
||||||
this.adapter = adapter;
|
this.adapter = adapter;
|
||||||
this.redoc = redoc;
|
this.redoc = redoc;
|
||||||
|
|
||||||
this.scrollParent = this.redoc.scrollParent;
|
this.scrollParent = this.redoc.scrollParent;
|
||||||
|
this.mobileNav = adapter.querySelector(elementRef.nativeElement, '.mobile-nav');
|
||||||
|
this.resourcesNav = adapter.querySelector(elementRef.nativeElement, '#resources-nav');
|
||||||
|
|
||||||
// for some reason constructor is not run inside zone
|
// for some reason constructor is not run inside zone
|
||||||
// as workaround running it manually
|
// as workaround running it manually
|
||||||
|
@ -45,6 +47,9 @@ export default class SideMenu extends BaseComponent {
|
||||||
this.prevOffsetY = null;
|
this.prevOffsetY = null;
|
||||||
|
|
||||||
redocEvents.bootstrapped.subscribe(() => this.hashScroll());
|
redocEvents.bootstrapped.subscribe(() => this.hashScroll());
|
||||||
|
|
||||||
|
this.activeCatCaption = '';
|
||||||
|
this.activeItemCaption = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollY() {
|
scrollY() {
|
||||||
|
@ -65,7 +70,12 @@ export default class SideMenu extends BaseComponent {
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
this.prevOffsetY = this.scrollY();
|
this.prevOffsetY = this.scrollY();
|
||||||
this.scrollYOffset = this.redoc.options.scrollYOffset;
|
|
||||||
|
//decorate option.scrollYOffset to account mobile nav
|
||||||
|
this.scrollYOffset = () => {
|
||||||
|
let mobileNavOffset = this.mobileNav.clientHeight;
|
||||||
|
return this.redoc.options.scrollYOffset() + mobileNavOffset;
|
||||||
|
};
|
||||||
this._cancel = {};
|
this._cancel = {};
|
||||||
this._cancel.scroll = this.adapter.onAndCancel(this.scrollParent, 'scroll', () => { this.scrollHandler(); });
|
this._cancel.scroll = this.adapter.onAndCancel(this.scrollParent, 'scroll', () => { this.scrollHandler(); });
|
||||||
this._cancel.hash = this.adapter.onAndCancel(global, 'hashchange', evt => this.hashScroll(evt));
|
this._cancel.hash = this.adapter.onAndCancel(global, 'hashchange', evt => this.hashScroll(evt));
|
||||||
|
@ -77,6 +87,9 @@ export default class SideMenu extends BaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
activateAndScroll(idx, methodIdx) {
|
activateAndScroll(idx, methodIdx) {
|
||||||
|
if (this.mobileMode()) {
|
||||||
|
this.toggleMobileNav();
|
||||||
|
}
|
||||||
this.activate(idx, methodIdx);
|
this.activate(idx, methodIdx);
|
||||||
this.scrollToActive();
|
this.scrollToActive();
|
||||||
}
|
}
|
||||||
|
@ -98,6 +111,10 @@ export default class SideMenu extends BaseComponent {
|
||||||
|
|
||||||
activate(catIdx, methodIdx) {
|
activate(catIdx, methodIdx) {
|
||||||
let menu = this.data.menu;
|
let menu = this.data.menu;
|
||||||
|
|
||||||
|
this.activeCatCaption = '';
|
||||||
|
this.activeItemCaption = '';
|
||||||
|
|
||||||
menu[this.activeCatIdx].active = false;
|
menu[this.activeCatIdx].active = false;
|
||||||
if (menu[this.activeCatIdx].methods.length) {
|
if (menu[this.activeCatIdx].methods.length) {
|
||||||
if (this.activeMethodIdx >= 0) {
|
if (this.activeMethodIdx >= 0) {
|
||||||
|
@ -108,11 +125,13 @@ export default class SideMenu extends BaseComponent {
|
||||||
this.activeCatIdx = catIdx;
|
this.activeCatIdx = catIdx;
|
||||||
this.activeMethodIdx = methodIdx;
|
this.activeMethodIdx = methodIdx;
|
||||||
menu[catIdx].active = true;
|
menu[catIdx].active = true;
|
||||||
|
this.activeCatCaption = menu[catIdx].name;
|
||||||
this.activeMethodPtr = null;
|
this.activeMethodPtr = null;
|
||||||
if (menu[catIdx].methods.length && (methodIdx > -1)) {
|
if (menu[catIdx].methods.length && (methodIdx > -1)) {
|
||||||
let currentItem = menu[catIdx].methods[methodIdx];
|
let currentItem = menu[catIdx].methods[methodIdx];
|
||||||
currentItem.active = true;
|
currentItem.active = true;
|
||||||
this.activeMethodPtr = currentItem.pointer;
|
this.activeMethodPtr = currentItem.pointer;
|
||||||
|
this.activeItemCaption = currentItem.summary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +218,26 @@ export default class SideMenu extends BaseComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mobileMode() {
|
||||||
|
return this.mobileNav.clientHeight > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMobileNav() {
|
||||||
|
let dom = this.adapter;
|
||||||
|
let overflowParent = (this.scrollParent === global) ? dom.defaultDoc().body : this.scrollParent;
|
||||||
|
if (dom.hasStyle(this.resourcesNav, 'height')) {
|
||||||
|
dom.removeStyle(this.resourcesNav, 'height');
|
||||||
|
dom.removeStyle(overflowParent, 'overflow-y');
|
||||||
|
} else {
|
||||||
|
let viewportHeight = this.scrollParent.innerHeight || this.scrollParent.clientHeight;
|
||||||
|
let height = viewportHeight - this.mobileNav.getBoundingClientRect().bottom;
|
||||||
|
dom.setStyle(overflowParent, 'overflow-y', 'hidden');
|
||||||
|
dom.setStyle(this.resourcesNav, 'height', height + 'px');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.changeActive(CHANGE.INITIAL);
|
this.changeActive(CHANGE.INITIAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SideMenu.parameters = SideMenu.parameters.concat([[BrowserDomAdapter], [NgZone]]);
|
SideMenu.parameters = SideMenu.parameters.concat([[ElementRef], [BrowserDomAdapter], [NgZone]]);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@import '../../common/styles/variables';
|
@import '../../common/styles/variables';
|
||||||
|
$mobile-menu-compact-breakpoint: 550px;
|
||||||
|
|
||||||
.menu-header {
|
.menu-header {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -9,7 +10,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-cat-header {
|
.menu-cat-header {
|
||||||
//font-weight: bold;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: $side-menu-item-color;
|
color: $side-menu-item-color;
|
||||||
|
@ -24,6 +24,10 @@
|
||||||
background-color: $side-menu-even-bg-color;
|
background-color: $side-menu-even-bg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-cat .menu-cat-header.active {
|
||||||
|
background-color: darken($side-menu-active-bg-color, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
.menu-subitems {
|
.menu-subitems {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -61,7 +65,89 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-cat-header.active, .menu-subitems li.active {
|
.menu-cat .menu-subitems {
|
||||||
background-color: $side-menu-active-bg-color !important;
|
li.active, li.active:nth-of-type(even) {
|
||||||
font-weight: bold;
|
background-color: $side-menu-active-bg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-nav {
|
||||||
|
display: none;
|
||||||
|
height: 3em;
|
||||||
|
line-height: 3em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 3em;
|
||||||
|
height: 3em;
|
||||||
|
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve"><polygon fill="#010101" points="23.1 34.1 51.5 61.7 80 34.1 81.5 35 51.5 64.1 21.5 35 23.1 34.1 "/></svg>');
|
||||||
|
background-size: 70%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
|
||||||
|
float: right;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-header {
|
||||||
|
padding: 0 10px 0 20px;
|
||||||
|
font-size: 0.95em;
|
||||||
|
|
||||||
|
@media (max-width: $mobile-menu-compact-breakpoint) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||||
|
.mobile-nav {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources-nav {
|
||||||
|
height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources-nav .menu-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-subitems {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-tag {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-endpoint:before {
|
||||||
|
content: "/";
|
||||||
|
padding: 0 2px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-endpoint:empty:before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-item-info {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 350px;
|
||||||
|
|
||||||
|
@media (max-width: $mobile-menu-compact-breakpoint) {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 20px;
|
||||||
|
max-width: 80%;
|
||||||
|
max-width: calc(100% - 4em);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
import {Component, View, OnInit, OnDestroy, ChangeDetectionStrategy} from 'angular2/core';
|
import {Component, View, OnInit, OnDestroy, ChangeDetectionStrategy} from 'angular2/core';
|
||||||
import {CORE_DIRECTIVES, JsonPipe} from 'angular2/common';
|
import {CORE_DIRECTIVES, JsonPipe, AsyncPipe} from 'angular2/common';
|
||||||
import SchemaManager from '../utils/SchemaManager';
|
import SchemaManager from '../utils/SchemaManager';
|
||||||
import JsonPointer from '../utils/JsonPointer';
|
import JsonPointer from '../utils/JsonPointer';
|
||||||
import {MarkedPipe, JsonPointerEscapePipe} from '../utils/pipes';
|
import {MarkedPipe, JsonPointerEscapePipe} from '../utils/pipes';
|
||||||
|
@ -49,7 +49,7 @@ function snapshot(obj) {
|
||||||
export function RedocComponent(options) {
|
export function RedocComponent(options) {
|
||||||
let inputs = safeConcat(options.inputs, commonInputs);
|
let inputs = safeConcat(options.inputs, commonInputs);
|
||||||
let directives = safeConcat(options.directives, CORE_DIRECTIVES);
|
let directives = safeConcat(options.directives, CORE_DIRECTIVES);
|
||||||
let pipes = safeConcat(options.pipes, [JsonPointerEscapePipe, MarkedPipe, JsonPipe]);
|
let pipes = safeConcat(options.pipes, [JsonPointerEscapePipe, MarkedPipe, JsonPipe, AsyncPipe]);
|
||||||
|
|
||||||
return function decorator(target) {
|
return function decorator(target) {
|
||||||
|
|
||||||
|
@ -104,7 +104,9 @@ export class BaseComponent {
|
||||||
let dereferencedCache = {};
|
let dereferencedCache = {};
|
||||||
|
|
||||||
let resolve = (schema) => {
|
let resolve = (schema) => {
|
||||||
|
let resolvedRef;
|
||||||
if (schema && schema.$ref) {
|
if (schema && schema.$ref) {
|
||||||
|
resolvedRef = schema.$ref;
|
||||||
let resolved = this.schemaMgr.byPointer(schema.$ref);
|
let resolved = this.schemaMgr.byPointer(schema.$ref);
|
||||||
let baseName = JsonPointer.baseName(schema.$ref);
|
let baseName = JsonPointer.baseName(schema.$ref);
|
||||||
if (!dereferencedCache[schema.$ref]) {
|
if (!dereferencedCache[schema.$ref]) {
|
||||||
|
@ -114,11 +116,12 @@ export class BaseComponent {
|
||||||
} else {
|
} else {
|
||||||
// for circular referenced save only title and type
|
// for circular referenced save only title and type
|
||||||
resolved = {
|
resolved = {
|
||||||
title: resolved.title
|
title: resolved.title,
|
||||||
|
type: resolved.type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dereferencedCache[schema.$ref] = true;
|
dereferencedCache[schema.$ref] = dereferencedCache[schema.$ref] ? dereferencedCache[schema.$ref] + 1 : 1;
|
||||||
|
|
||||||
resolved.title = resolved.title || baseName;
|
resolved.title = resolved.title || baseName;
|
||||||
|
|
||||||
|
@ -133,7 +136,6 @@ export class BaseComponent {
|
||||||
schema = schema.description ? {
|
schema = schema.description ? {
|
||||||
description: schema.description
|
description: schema.description
|
||||||
} : {};
|
} : {};
|
||||||
//for (var prop in schema) delete schema[prop];
|
|
||||||
Object.assign(schema, resolved);
|
Object.assign(schema, resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,10 +145,11 @@ export class BaseComponent {
|
||||||
schema[key] = resolve(value);
|
schema[key] = resolve(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (resolvedRef) dereferencedCache[resolvedRef] = dereferencedCache[resolvedRef] ? dereferencedCache[resolvedRef] - 1 : 0;
|
||||||
return schema;
|
return schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.componentSchema = resolve(schema);
|
this.componentSchema = resolve(schema, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
joinAllOf(schema = this.componentSchema, opts) {
|
joinAllOf(schema = this.componentSchema, opts) {
|
||||||
|
|
|
@ -14,12 +14,17 @@ export default class OptionsManager {
|
||||||
OptionsManager.prototype._instance = this;
|
OptionsManager.prototype._instance = this;
|
||||||
|
|
||||||
this._defaults = {
|
this._defaults = {
|
||||||
scrollYOffset: 0
|
scrollYOffset: 0,
|
||||||
|
disableLazySchemas: false
|
||||||
};
|
};
|
||||||
|
|
||||||
this._options = {};
|
this._options = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static instance() {
|
||||||
|
return new OptionsManager();
|
||||||
|
}
|
||||||
|
|
||||||
get options() {
|
get options() {
|
||||||
return this._options;
|
return this._options;
|
||||||
}
|
}
|
||||||
|
|
106
lib/utils/JsonFormatterPipe.js
Normal file
106
lib/utils/JsonFormatterPipe.js
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
'use strict';
|
||||||
|
import {Pipe} from 'angular2/core';
|
||||||
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
var level = 1;
|
||||||
|
const COLLAPSE_LEVEL = 2;
|
||||||
|
|
||||||
|
@Pipe({ name: 'jsonFormatter' })
|
||||||
|
export class JsonFormatter {
|
||||||
|
transform(value) {
|
||||||
|
if (isBlank(value)) return value;
|
||||||
|
return jsonToHTML(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function htmlEncode(t) {
|
||||||
|
return t != null ? t.toString().replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function decorateWithSpan(value, className) {
|
||||||
|
return '<span class="' + className + '">' + htmlEncode(value) + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function valueToHTML(value) {
|
||||||
|
var valueType = typeof value, output = '';
|
||||||
|
if (value == null) {
|
||||||
|
output += decorateWithSpan('null', 'type-null');
|
||||||
|
}
|
||||||
|
else if (value && value.constructor === Array) {
|
||||||
|
level++;
|
||||||
|
output += arrayToHTML(value);
|
||||||
|
level--;
|
||||||
|
}
|
||||||
|
else if (valueType === 'object') {
|
||||||
|
level++;
|
||||||
|
output += objectToHTML(value);
|
||||||
|
level--;
|
||||||
|
}
|
||||||
|
else if (valueType === 'number') {
|
||||||
|
output += decorateWithSpan(value, 'type-number');
|
||||||
|
}
|
||||||
|
else if (valueType === 'string') {
|
||||||
|
if (/^(http|https):\/\/[^\\s]+$/.test(value)) {
|
||||||
|
output += decorateWithSpan('"', 'type-string') + '<a href="' + value + '">' + htmlEncode(value) + '</a>' + decorateWithSpan('"', 'type-string');
|
||||||
|
} else {
|
||||||
|
output += decorateWithSpan('"' + value + '"', 'type-string');
|
||||||
|
}
|
||||||
|
} else if (valueType === 'boolean') {
|
||||||
|
output += decorateWithSpan(value, 'type-boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayToHTML(json) {
|
||||||
|
var collapsed = level > COLLAPSE_LEVEL ? 'collapsed' : '';
|
||||||
|
var i, length;
|
||||||
|
var output = '<div class="collapser"></div>[<span class="ellipsis"></span><ul class="array collapsible">';
|
||||||
|
var hasContents = false;
|
||||||
|
for (i = 0, length = json.length; i < length; i++) {
|
||||||
|
hasContents = true;
|
||||||
|
output += '<li><div class="hoverable ' + collapsed + '">';
|
||||||
|
output += valueToHTML(json[i]);
|
||||||
|
if (i < length - 1) {
|
||||||
|
output += ',';
|
||||||
|
}
|
||||||
|
output += '</div></li>';
|
||||||
|
}
|
||||||
|
output += '</ul>]';
|
||||||
|
if (!hasContents) {
|
||||||
|
output = '[ ]';
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectToHTML(json) {
|
||||||
|
var collapsed = level > COLLAPSE_LEVEL ? 'collapsed' : '';
|
||||||
|
var i, key, length, keys = Object.keys(json);
|
||||||
|
var output = '<div class="collapser"></div>{<span class="ellipsis"></span><ul class="obj collapsible">';
|
||||||
|
var hasContents = false;
|
||||||
|
for (i = 0, length = keys.length; i < length; i++) {
|
||||||
|
key = keys[i];
|
||||||
|
hasContents = true;
|
||||||
|
output += '<li><div class="hoverable ' + collapsed + '">';
|
||||||
|
output += '<span class="property">' + htmlEncode(key) + '</span>: ';
|
||||||
|
output += valueToHTML(json[key]);
|
||||||
|
if (i < length - 1) {
|
||||||
|
output += ',';
|
||||||
|
}
|
||||||
|
output += '</div></li>';
|
||||||
|
}
|
||||||
|
output += '</ul>}';
|
||||||
|
if (!hasContents) {
|
||||||
|
output = '{ }';
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsonToHTML(json) {
|
||||||
|
level = 1;
|
||||||
|
var output = '';
|
||||||
|
output += '<div class="redoc-json">';
|
||||||
|
output += valueToHTML(json);
|
||||||
|
output += '</div>';
|
||||||
|
return output;
|
||||||
|
}
|
|
@ -62,6 +62,6 @@ export class MarkedPipe {
|
||||||
if (!isString(value)) {
|
if (!isString(value)) {
|
||||||
throw new InvalidPipeArgumentException(JsonPointerEscapePipe, value);
|
throw new InvalidPipeArgumentException(JsonPointerEscapePipe, value);
|
||||||
}
|
}
|
||||||
return marked(value);
|
return `<span class="redoc-markdown-block">${marked(value)}</span>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"description": "Swagger-generated API Reference Documentation",
|
"description": "Swagger-generated API Reference Documentation",
|
||||||
"version": "0.3.0",
|
"version": "0.4.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/Rebilly/ReDoc"
|
"url": "git://github.com/Rebilly/ReDoc"
|
||||||
},
|
},
|
||||||
"main": "dist/redoc.full.min.js",
|
"main": "dist/redoc.min.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "gulp lint && ./build/run_tests.sh",
|
"test": "gulp lint && ./build/run_tests.sh",
|
||||||
"prepublish": "gulp build",
|
"prepublish": "gulp build",
|
||||||
|
|
|
@ -65,7 +65,7 @@ function verifyNoBrowserErrors() {
|
||||||
function scrollToEl(selector) {
|
function scrollToEl(selector) {
|
||||||
let script = `
|
let script = `
|
||||||
document.querySelector('${selector}').scrollIntoView(true);
|
document.querySelector('${selector}').scrollIntoView(true);
|
||||||
window.scrollBy(0, 10);
|
window.scrollBy(0, 200);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return browser.driver.executeScript(script);
|
return browser.driver.executeScript(script);
|
||||||
|
|
|
@ -10,12 +10,12 @@
|
||||||
</redoc>
|
</redoc>
|
||||||
|
|
||||||
<!-- ReDoc built file with all dependencies included -->
|
<!-- ReDoc built file with all dependencies included -->
|
||||||
<script src="dist/redoc.full.min.js"> </script>
|
<script src="dist/redoc.min.js"> </script>
|
||||||
<script>
|
<script>
|
||||||
window.redocError = null;
|
window.redocError = null;
|
||||||
/* init redoc */
|
/* init redoc */
|
||||||
var url = window.location.search.substr(5) || 'swagger.json';
|
var url = window.location.search.substr(5) || 'swagger.json';
|
||||||
Redoc.init(decodeURIComponent(url)).then(function() {}, function(err) {
|
Redoc.init(decodeURIComponent(url), {disableLazySchemas: true}).then(function() {}, function(err) {
|
||||||
window.redocError = err;
|
window.redocError = err;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -44,7 +44,8 @@ describe('Scroll sync', () => {
|
||||||
|
|
||||||
it('should update active menu entries on page scroll', () => {
|
it('should update active menu entries on page scroll', () => {
|
||||||
scrollToEl('[tag="store"]').then(function() {
|
scrollToEl('[tag="store"]').then(function() {
|
||||||
expect($('.menu-cat-header.active').getText()).toBe('STORE');
|
expect($('.menu-cat-header.active').getInnerHtml()).toContain('store');
|
||||||
|
expect($('.selected-tag').getInnerHtml()).toContain('store');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,13 @@ export function getChildDebugElement(parent, tagName) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a child DebugElement by Component Type. */
|
||||||
|
export function getChildDebugElementByType(parent, type) {
|
||||||
|
return parent.query(debugEl => {
|
||||||
|
return debugEl.componentInstance instanceof type;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** Gets a child DebugElements by tag name. */
|
/** Gets a child DebugElements by tag name. */
|
||||||
export function getChildDebugElementAll(parent, tagName) {
|
export function getChildDebugElementAll(parent, tagName) {
|
||||||
return parent.queryAll(debugEl => {
|
return parent.queryAll(debugEl => {
|
||||||
|
|
|
@ -85,12 +85,12 @@ describe('Pipes', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
unmarked = 'test\n';
|
unmarked = 'test\n';
|
||||||
marked = '<p>test</p>\n';
|
marked = '<span class="redoc-markdown-block"><p>test</p>\n</span>';
|
||||||
pipe = new MarkedPipe();
|
pipe = new MarkedPipe();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('MarkedPipe transform', () => {
|
describe('MarkedPipe transform', () => {
|
||||||
it('should escpae pointer', () => {
|
it('should wrap in markdown span', () => {
|
||||||
var val = pipe.transform(unmarked);
|
var val = pipe.transform(unmarked);
|
||||||
val.should.be.equal(marked);
|
val.should.be.equal(marked);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user