Merge commit '1e60b7abd47b37561c8ab7f5f18ab6a6cc5868c2' into releases

This commit is contained in:
RedocBot 2016-07-17 15:35:10 +00:00 committed by travis@localhost
commit df49090af8
17 changed files with 340 additions and 66 deletions

View File

@ -50,34 +50,6 @@ gulp.task('tsc', function() {
exec('tsc -p ./tsconfig.json'); exec('tsc -p ./tsconfig.json');
}); });
// function compileTs(files, es5) {
// var tsProject = ts.createProject('tsconfig.json');
// var allFiles = [].concat(files, ['typings/**/*.d.ts']);
// var res = gulp.src(allFiles, {
// base: config.src,
// outDir: config.tmp
// })
// .pipe(tslint())
// .pipe(tslint.report('prose', {
// summarizeFailureOutput: true,
// emitError: !watchMode
// }))
// .pipe(sourcemaps.init())
// .pipe(ts(tsProject))
// .on('error', function () {
// if (watchMode) {
// return;
// }
// process.exit(1);
// });
// return res.js
// .pipe(sourcemaps.write('.', {
// includeContent: inline
// }))
// .pipe(gulp.dest(config.tmp));
// }
gulp.task('inlineTemplates', ['sass'], function() { gulp.task('inlineTemplates', ['sass'], function() {
return gulp.src('.tmp/**/*.js', { base: './tmp' }) return gulp.src('.tmp/**/*.js', { base: './tmp' })
.pipe(replace(/'(.*?)\.css'/g, '\'$1.scss\'')) .pipe(replace(/'(.*?)\.css'/g, '\'$1.scss\''))

View File

@ -5,48 +5,50 @@ $cell-spacing: 25px;
$cell-padding: 10px; $cell-padding: 10px;
$bullet-margin: 10px; $bullet-margin: 10px;
$line-border: $lines-width solid $tree-lines-color; $line-border: $lines-width solid $tree-lines-color;
$line-border-erase: ($lines-width + 1px) solid white; $line-border-erase: ($lines-width + 1px) solid #fff;
$param-name-height: 20px; $param-name-height: 20px;
$sub-schema-offset: ($bullet-size / 2) + $bullet-margin; $sub-schema-offset: ($bullet-size / 2) + $bullet-margin;
.param-name { .param-name {
font-size: 0.929em;
padding: $cell-padding 0 $cell-padding 0;
font-weight: $regular;
box-sizing: border-box;
line-height: $param-name-height;
border-left: $line-border;
white-space: nowrap;
position: relative; position: relative;
border-left: $line-border;
padding: $cell-padding 0;
vertical-align: top; vertical-align: top;
line-height: $param-name-height;
white-space: nowrap;
font-size: 0.929em;
font-weight: $regular;
box-sizing: border-box;
} }
.param-name-wrap { .param-name-wrap {
padding-right: $cell-spacing;
display: inline-block; display: inline-block;
padding-right: $cell-spacing;
font-family: $headers-font, $headers-font-family; font-family: $headers-font, $headers-font-family;
} }
.param-info { .param-info {
padding: $cell-padding 0;
box-sizing: border-box;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
padding: $cell-padding 0;
width: 75%; width: 75%;
line-height: 1em; line-height: 1em;
box-sizing: border-box;
} }
.param-range { .param-range {
color: rgba($primary-color, .7);
position: relative; position: relative;
top: 1px; top: 1px;
padding: 0 4px; margin-right: 6px;
margin-left: 6px;
border-radius: $border-radius; border-radius: $border-radius;
background-color: rgba($primary-color, .1); background-color: rgba($primary-color, .1);
margin-left: 6px; padding: 0 4px;
margin-right: 6px; color: rgba($primary-color, 0.7);
} }
.param-description { .param-description {
@ -54,18 +56,18 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
} }
.param-required { .param-required {
color: red;
font-weight: bold;
font-size: 12px;
line-height: $param-name-height;
vertical-align: middle; vertical-align: middle;
line-height: $param-name-height;
color: #f00;
font-size: 12px;
font-weight: bold;
} }
.param-type { .param-type {
vertical-align: middle;
line-height: $param-name-height;
color: rgba($black, 0.4); color: rgba($black, 0.4);
font-size: 0.929em; font-size: 0.929em;
line-height: $param-name-height;
vertical-align: middle;
font-weight: normal; font-weight: normal;
} }
@ -158,11 +160,11 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
font-size: 13px; font-size: 13px;
&:before { &:before {
content: "Values: {" content: 'Values: {';
} }
&:after { &:after {
content: "}" content: '}';
} }
> .enum-value { > .enum-value {

View File

@ -14,10 +14,14 @@
</div> </div>
<div class="method-samples"> <div class="method-samples">
<h5>Definition</h5> <h5>Definition</h5>
<span class="method-endpoint">
<div class="method-endpoint">
<h5 class="http-method" [ngClass]="data.httpMethod">{{data.httpMethod}}</h5> <h5 class="http-method" [ngClass]="data.httpMethod">{{data.httpMethod}}</h5>
<span class="api-url">{{data.apiUrl}}</span><span class="path">{{data.path}}</span> <span select-on-click><!--
</span> --><span class="api-url">{{data.apiUrl}}</span><span class="path">{{data.path}}</span><!--
--></span>
</div>
<div *ngIf="data.bodyParam"> <div *ngIf="data.bodyParam">
<br> <br>
<request-samples [pointer]="pointer" [schemaPointer]="data.bodyParam._pointer"> <request-samples [pointer]="pointer" [schemaPointer]="data.bodyParam._pointer">

View File

@ -134,6 +134,10 @@ responses-samples {
text-transform: uppercase; text-transform: uppercase;
} }
[select-on-click] {
cursor: pointer;
}
@media (max-width: 1100px) { @media (max-width: 1100px) {
.methods:before { .methods:before {
display: none; display: none;

View File

@ -3,6 +3,8 @@ import { Input } from '@angular/core';
import JsonPointer from '../../utils/JsonPointer'; import JsonPointer from '../../utils/JsonPointer';
import { RedocComponent, BaseComponent, SpecManager} from '../base'; import { RedocComponent, BaseComponent, SpecManager} from '../base';
import { SelectOnClick } from '../../shared/components/SelectOnClick/select-on-click.directive';
import { ParamsList } from '../ParamsList/params-list'; import { ParamsList } from '../ParamsList/params-list';
import { ResponsesList } from '../ResponsesList/responses-list'; import { ResponsesList } from '../ResponsesList/responses-list';
import { ResponsesSamples } from '../ResponsesSamples/responses-samples'; import { ResponsesSamples } from '../ResponsesSamples/responses-samples';
@ -14,7 +16,7 @@ import { SchemaHelper } from '../../services/schema-helper.service';
selector: 'method', selector: 'method',
templateUrl: './method.html', templateUrl: './method.html',
styleUrls: ['./method.css'], styleUrls: ['./method.css'],
directives: [ ParamsList, ResponsesList, ResponsesSamples, SchemaSample, RequestSamples ], directives: [ ParamsList, ResponsesList, ResponsesSamples, SchemaSample, RequestSamples, SelectOnClick ],
detect: true detect: true
}) })
export class Method extends BaseComponent { export class Method extends BaseComponent {

View File

@ -139,6 +139,17 @@ api-logo {
color: $red; color: $red;
border: 1px solid rgba(38,50,56,0.1); border: 1px solid rgba(38,50,56,0.1);
} }
.hint--inversed {
&:before {
border-top-color: #fff;
}
&:after {
background: #fff;
color: #383838;
}
}
} }
footer { footer {

View File

@ -5,6 +5,11 @@
<schema-sample [pointer]="data.schemaPointer" [skipReadOnly]="true"> </schema-sample> <schema-sample [pointer]="data.schemaPointer" [skipReadOnly]="true"> </schema-sample>
</tab> </tab>
<tab *ngFor="let sample of data.samples" [tabTitle]="sample.lang"> <tab *ngFor="let sample of data.samples" [tabTitle]="sample.lang">
<div class="code-sample">
<div class="action-buttons">
<span copy-button [copyText]="sample.source" class="hint--top hint--inversed"> <a>Copy</a> </span>
</div>
<pre [innerHtml]="sample.source | prism:sample.lang"></pre> <pre [innerHtml]="sample.source | prism:sample.lang"></pre>
</div>
</tab> </tab>
</tabs> </tabs>

View File

@ -1,5 +1,37 @@
@import '../../shared/styles/variables'; @import '../../shared/styles/variables';
.action-buttons {
display: block;
opacity: 0;
transition: opacity 0.3s ease;
transform: translateY(100%);
> span {
float: right;
}
> span > a {
padding: 2px 10px;
color: #ffffff;
cursor: pointer;
background-color: darken($black, 4%);
&:hover {
background-color: $black;
}
}
&:after {
display: block;
content: '';
clear: both;
}
}
.code-sample:hover > .action-buttons {
opacity: 1;
}
header { header {
font-family: $headers-font; font-family: $headers-font;
font-size: $h5; font-size: $h5;
@ -39,4 +71,10 @@ pre {
word-break: break-all; word-break: break-all;
word-wrap: break-word; word-wrap: break-word;
white-space: pre-wrap; white-space: pre-wrap;
margin-top: 0;
overflow-x: auto;
padding: 20px;
border-radius: 4px;
background-color: #222d32;
margin-bottom: 36px;
} }

View File

@ -9,11 +9,13 @@ import { SchemaSample } from '../SchemaSample/schema-sample';
import { PrismPipe } from '../../utils/pipes'; import { PrismPipe } from '../../utils/pipes';
import { RedocEventsService } from '../../services/index'; import { RedocEventsService } from '../../services/index';
import { CopyButton } from '../../shared/components/CopyButton/copy-button.directive';
@RedocComponent({ @RedocComponent({
selector: 'request-samples', selector: 'request-samples',
templateUrl: './request-samples.html', templateUrl: './request-samples.html',
styleUrls: ['./request-samples.css'], styleUrls: ['./request-samples.css'],
directives: [SchemaSample, Tabs, Tab], directives: [SchemaSample, Tabs, Tab, CopyButton],
inputs: ['schemaPointer'], inputs: ['schemaPointer'],
pipes: [PrismPipe], pipes: [PrismPipe],
detect: true, detect: true,

View File

@ -1,5 +1,10 @@
<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 == undefined"> Sample unavailable </pre> <pre *ngIf="data.sample == undefined"> Sample unavailable </pre>
<div class="action-buttons">
<span> <a *ngIf="enableButtons" (click)="collapseAll()">Collapse all</a> </span>
<span> <a *ngIf="enableButtons" (click)="expandAll()">Expand all</a> </span>
<span copy-button [copyText]="data.sample | json" class="hint--top hint--inversed"> <a>Copy</a> </span>
</div>
<pre [innerHtml]="data.sample | jsonFormatter"></pre> <pre [innerHtml]="data.sample | jsonFormatter"></pre>
</div> </div>

View File

@ -3,7 +3,56 @@
pre { pre {
background-color: transparent; background-color: transparent;
padding: 0; padding: 0;
margin: 0;
clear: both;
} }
.action-buttons {
display: block;
opacity: 0;
transition: opacity 0.3s ease;
transform: translateY(100%);
> span {
float: right;
}
> span > a {
padding: 2px 10px;
color: #ffffff;
cursor: pointer;
&:before {
content: '|';
display: inline-block;
transform: translateX(-10px);
}
&:last-child:before {
display: none;
margin-left: 0;
}
&:first-child {
margin-right: 0;
}
&:hover {
background-color: $black;
}
}
&:after {
display: block;
content: '';
clear: both;
}
}
.snippet:hover .action-buttons {
opacity: 1;
}
:host { :host {
.property { .property {
//font-weight: bold; //font-weight: bold;

View File

@ -8,15 +8,19 @@ import { RedocComponent, BaseComponent, SpecManager } from '../base';
import { JsonFormatter } from '../../utils/JsonFormatterPipe'; import { JsonFormatter } from '../../utils/JsonFormatterPipe';
import { SchemaNormalizer } from '../../services/schema-normalizer.service'; import { SchemaNormalizer } from '../../services/schema-normalizer.service';
import { CopyButton } from '../../shared/components/CopyButton/copy-button.directive';
@RedocComponent({ @RedocComponent({
selector: 'schema-sample', selector: 'schema-sample',
templateUrl: './schema-sample.html', templateUrl: './schema-sample.html',
pipes: [JsonFormatter], pipes: [JsonFormatter],
directives: [CopyButton],
styleUrls: ['./schema-sample.css'] styleUrls: ['./schema-sample.css']
}) })
export class SchemaSample extends BaseComponent { export class SchemaSample extends BaseComponent {
element: any; element: any;
data: any; data: any;
enableButtons: boolean = false;
@Input() skipReadOnly:boolean; @Input() skipReadOnly:boolean;
private _normalizer:SchemaNormalizer; private _normalizer:SchemaNormalizer;
@ -74,6 +78,10 @@ export class SchemaSample extends BaseComponent {
} }
this.cache(sample); this.cache(sample);
this.data.sample = sample; this.data.sample = sample;
if (typeof sample === 'object') {
this.enableButtons = true;
}
} }
cache(sample) { cache(sample) {
@ -108,4 +116,21 @@ export class SchemaSample extends BaseComponent {
} }
}); });
} }
expandAll() {
let elements = this.element.getElementsByClassName('collapsible');
for (let i = 0; i < elements.length; i++) {
let collapsed = elements[i];
collapsed.parentNode.classList.remove('collapsed');
}
}
collapseAll() {
let elements = this.element.getElementsByClassName('collapsible');
for (let i = 0; i < elements.length; i++) {
let expanded = elements[i];
if (expanded.parentNode.classList.contains('redoc-json')) continue;
expanded.parentNode.classList.add('collapsed');
}
}
} }

View File

@ -0,0 +1,83 @@
'use strict';
export class Clipboard {
static isSupported():boolean {
return document.queryCommandSupported && document.queryCommandSupported('copy');
}
static selectElement(element:any):void {
let range;
let selection;
if ((<any>document.body).createTextRange) {
range = (<any>document.body).createTextRange();
range.moveToElementText(element);
range.select();
} else if (document.createRange && window.getSelection) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
}
static deselect():void {
if ( (<any>document).selection ) {
(<any>document).selection.empty();
} else if ( window.getSelection ) {
window.getSelection().removeAllRanges();
}
}
static copySelected():boolean {
let result;
try {
result = document.execCommand('copy');
} catch (err) {
result = false;
}
return result;
}
static copyElement(element:any):boolean {
Clipboard.selectElement(element);
let res = Clipboard.copySelected();
if (res) Clipboard.deselect();
return res;
}
static copyCustom(text:string):boolean {
let textArea = document.createElement('textarea');
textArea.style.position = 'fixed';
textArea.style.top = '0';
textArea.style.left = '0';
// Ensure it has a small width and height. Setting to 1px / 1em
// doesn't work as this gives a negative w/h on some browsers.
textArea.style.width = '2em';
textArea.style.height = '2em';
// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = '0';
// Clean up any borders.
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
// Avoid flash of white box if rendered for any reason.
textArea.style.background = 'transparent';
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
let res = Clipboard.copySelected();
document.body.removeChild(textArea);
return res;
}
}

View File

@ -0,0 +1,52 @@
'use strict';
import { Directive, Input, HostListener, Renderer, ElementRef, OnInit} from '@angular/core';
import { Clipboard } from '../../../services/clipboard.service';
@Directive({
selector: '[copy-button]'
})
export class CopyButton implements OnInit {
$element: any;
cancelScrollBinding: any;
$redocEl: any;
@Input() copyText: string;
@Input() copyElement:any;
@Input() hintElement:any;
constructor(private renderer: Renderer, private element: ElementRef) {}
ngOnInit () {
if (!Clipboard.isSupported()) {
this.element.nativeElement.parentNode.removeChild(this.element.nativeElement);
}
this.renderer.setElementAttribute(this.element.nativeElement, 'data-hint', 'Copy to Clipboard!');
}
@HostListener('click')
onClick() {
let copied;
if (this.copyText) {
copied = Clipboard.copyCustom(this.copyText);
} else {
copied = Clipboard.copyElement(this.copyElement);
}
if (copied) {
this.renderer.setElementAttribute(this.element.nativeElement, 'data-hint', 'Copied!');
} else {
let hintElem = this.hintElement || this.copyElement;
if (!hintElem) return;
this.renderer.setElementAttribute(hintElem, 'data-hint', 'Press "ctrl + c" to copy');
this.renderer.setElementClass(hintElem, 'hint--top', true);
this.renderer.setElementClass(hintElem, 'hint--always', true);
}
}
@HostListener('mouseleave')
onLeave() {
setTimeout(() => {
this.renderer.setElementAttribute(this.element.nativeElement, 'data-hint', 'Copy to Clipboard');
}, 500);
}
}

View File

@ -0,0 +1,17 @@
'use strict';
import { Directive, HostListener, ElementRef} from '@angular/core';
import { Clipboard } from '../../../services/clipboard.service';
@Directive({
selector: '[select-on-click]'
})
export class SelectOnClick {
$element: any;
constructor(private element: ElementRef) {}
@HostListener('click')
onClick() {
Clipboard.selectElement(this.element.nativeElement);
}
}

View File

@ -1,7 +1,7 @@
{ {
"name": "redoc", "name": "redoc",
"description": "Swagger-generated API Reference Documentation", "description": "Swagger-generated API Reference Documentation",
"version": "0.15.3", "version": "0.16.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/Rebilly/ReDoc" "url": "git://github.com/Rebilly/ReDoc"

View File

@ -50,18 +50,21 @@ if (travis) {
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
build: process.env.TRAVIS_BUILD_NUMBER, build: process.env.TRAVIS_BUILD_NUMBER,
name: 'Redoc Safari Latest/OSX build ' + process.env.TRAVIS_BUILD_NUMBER, name: 'Redoc Safari Latest/OSX build ' + process.env.TRAVIS_BUILD_NUMBER,
idleTimeout: 180 idleTimeout: 180,
maxDuration: 1800*2
},{ },{
browserName: 'firefox', browserName: 'firefox',
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
build: process.env.TRAVIS_BUILD_NUMBER, build: process.env.TRAVIS_BUILD_NUMBER,
name: 'Redoc Firefox Latest/Win build ' + process.env.TRAVIS_BUILD_NUMBER name: 'Redoc Firefox Latest/Win build ' + process.env.TRAVIS_BUILD_NUMBER,
maxDuration: 1800*2
},{ },{
browserName: 'internet explorer', browserName: 'internet explorer',
version: '11.0', version: '11.0',
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
build: process.env.TRAVIS_BUILD_NUMBER, build: process.env.TRAVIS_BUILD_NUMBER,
name: 'Redoc IE11/Win build ' + process.env.TRAVIS_BUILD_NUMBER name: 'Redoc IE11/Win build ' + process.env.TRAVIS_BUILD_NUMBER,
maxDuration: 1800*2
}]; }];
} else { } else {
config.directConnect = true; config.directConnect = true;