From 91e7af2b9dbfdd2b12e2f7f29135f2d4921c6fe2 Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Sat, 23 Jan 2016 23:35:44 +0200 Subject: [PATCH] schema samples highlighting + collapsability --- .../JsonSchema/json-schema-common.scss | 10 +- .../ResponsesSamples/responses-samples.scss | 2 +- .../SchemaSample/schema-sample.html | 2 +- lib/components/SchemaSample/schema-sample.js | 28 +++-- .../SchemaSample/schema-sample.scss | 112 ++++++++++++++++++ lib/components/base.js | 4 +- lib/utils/JsonFormatterPipe.js | 106 +++++++++++++++++ 7 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 lib/components/SchemaSample/schema-sample.scss create mode 100644 lib/utils/JsonFormatterPipe.js diff --git a/lib/components/JsonSchema/json-schema-common.scss b/lib/components/JsonSchema/json-schema-common.scss index 44ccf1e4..434b63f3 100644 --- a/lib/components/JsonSchema/json-schema-common.scss +++ b/lib/components/JsonSchema/json-schema-common.scss @@ -76,12 +76,16 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin; color: rgba(0, 80, 0, 0.7); } -.param-type.integer { - color: rgba(30, 0, 80, 0.7); +.param-type.integer, .param-type.number { + color: rgba(74, 139, 179, 0.8); } .param-type.object { - color: rgba(139, 0, 0, 0.67); + color: rgba(0, 50, 159, 0.7); +} + +.param-type.boolean { + color: firebrick; } .param-type.with-hint { diff --git a/lib/components/ResponsesSamples/responses-samples.scss b/lib/components/ResponsesSamples/responses-samples.scss index 8a1840f4..9d33323f 100644 --- a/lib/components/ResponsesSamples/responses-samples.scss +++ b/lib/components/ResponsesSamples/responses-samples.scss @@ -16,7 +16,7 @@ header { font-weight: normal; } -:host tabs li { +:host > tabs > ul li { font-size: 13px; margin: 2px 0; padding: 2px 5px; diff --git a/lib/components/SchemaSample/schema-sample.html b/lib/components/SchemaSample/schema-sample.html index f01cfb59..9fe51cae 100644 --- a/lib/components/SchemaSample/schema-sample.html +++ b/lib/components/SchemaSample/schema-sample.html @@ -1,5 +1,5 @@
 Sample unavailable 
-
{{data.sample | json}}
+

 
diff --git a/lib/components/SchemaSample/schema-sample.js b/lib/components/SchemaSample/schema-sample.js index 6d5fafc9..79382e53 100644 --- a/lib/components/SchemaSample/schema-sample.js +++ b/lib/components/SchemaSample/schema-sample.js @@ -4,19 +4,19 @@ import {RedocComponent, BaseComponent} from '../base'; import SchemaSampler from 'json-schema-instantiator'; +import {JsonFormatter} from '../../utils/JsonFormatterPipe'; +import {ElementRef} from 'angular2/core'; + @RedocComponent({ selector: 'schema-sample', templateUrl: './lib/components/SchemaSample/schema-sample.html', - styles: [` - pre { - background-color: transparent; - padding: 0; - } - `] + pipes: [JsonFormatter], + styleUrls: ['./lib/components/SchemaSample/schema-sample.css'] }) export default class SchemaSample extends BaseComponent { - constructor(schemaMgr) { + constructor(schemaMgr, elementRef) { super(schemaMgr); + this.element = elementRef.nativeElement; } init() { @@ -44,5 +44,19 @@ export default class SchemaSample extends BaseComponent { } 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]]); diff --git a/lib/components/SchemaSample/schema-sample.scss b/lib/components/SchemaSample/schema-sample.scss new file mode 100644 index 00000000..3d5bb3fc --- /dev/null +++ b/lib/components/SchemaSample/schema-sample.scss @@ -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; + } +} diff --git a/lib/components/base.js b/lib/components/base.js index 131678f9..c27c7049 100644 --- a/lib/components/base.js +++ b/lib/components/base.js @@ -1,6 +1,6 @@ 'use strict'; 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 JsonPointer from '../utils/JsonPointer'; import {MarkedPipe, JsonPointerEscapePipe} from '../utils/pipes'; @@ -49,7 +49,7 @@ function snapshot(obj) { export function RedocComponent(options) { let inputs = safeConcat(options.inputs, commonInputs); 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) { diff --git a/lib/utils/JsonFormatterPipe.js b/lib/utils/JsonFormatterPipe.js new file mode 100644 index 00000000..11971097 --- /dev/null +++ b/lib/utils/JsonFormatterPipe.js @@ -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, '>') : ''; +} + +function decorateWithSpan(value, className) { + return '' + htmlEncode(value) + ''; +} + +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') + '' + htmlEncode(value) + '' + 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 = '
[]'; + if (!hasContents) { + output = '[ ]'; + } + return output; +} + +function objectToHTML(json) { + var collapsed = level > COLLAPSE_LEVEL ? 'collapsed' : ''; + var i, key, length, keys = Object.keys(json); + var output = '
{}'; + if (!hasContents) { + output = '{ }'; + } + return output; +} + +function jsonToHTML(json) { + level = 1; + var output = ''; + output += '
'; + output += valueToHTML(json); + output += '
'; + return output; +}