openapi-explorer
Version:
OpenAPI Explorer - API viewer with dynamically generated components, documentation, and interaction console
202 lines (197 loc) • 15.5 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.default = void 0;
var _lit = require("lit");
var _commonUtils = require("../utils/common-utils.js");
var _unsafeHtml = require("lit/directives/unsafe-html.js");
var _index = require("../languages/index.js");
var _fontStyles = _interopRequireDefault(require("../styles/font-styles.js"));
var _schemaStyles = _interopRequireDefault(require("../styles/schema-styles.js"));
var _borderStyles = _interopRequireDefault(require("../styles/border-styles.js"));
var _keyFrameStyles = _interopRequireDefault(require("../styles/key-frame-styles.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
class SchemaTree extends _lit.LitElement {
static get properties() {
return {
data: {
type: Object
},
schemaExpandLevel: {
type: Number,
attribute: 'schema-expand-level'
},
schemaDescriptionExpanded: {
type: Boolean
},
schemaHideReadOnly: {
type: String,
attribute: 'schema-hide-read-only'
},
schemaHideWriteOnly: {
type: String,
attribute: 'schema-hide-write-only'
}
};
}
connectedCallback() {
super.connectedCallback();
if (!this.schemaExpandLevel || this.schemaExpandLevel < 1) {
this.schemaExpandLevel = 99999;
}
this.schemaDescriptionExpanded = true;
if (!this.schemaHideReadOnly || !'true false'.includes(this.schemaHideReadOnly)) {
this.schemaHideReadOnly = 'true';
}
if (!this.schemaHideWriteOnly || !'true false'.includes(this.schemaHideWriteOnly)) {
this.schemaHideWriteOnly = 'true';
}
}
/**
* @param {Map<string, object>} changedProperties Changed Properties
*/
update(changedProperties) {
if (changedProperties.has('data')) {
this.interactive = false;
}
super.update(changedProperties);
}
updated() {
this.interactive = true; // Note: interactive is not a reactive property
}
static finalizeStyles() {
return [_fontStyles.default, _schemaStyles.default, _borderStyles.default, _keyFrameStyles.default, (0, _lit.css)`.tree{min-height:30px;background:var(--bg2);padding:12px;font-size:var(--font-size-small);text-align:left;line-height:calc(var(--font-size-small) + 6px)}.tree .key{max-width:300px}.requiredStar::after{content:'*';color:var(--red);font-size:larger}.key.deprecated .key-label{text-decoration:line-through}.open-bracket{display:inline-block;padding:0 20px 0 0;cursor:pointer;border:1px solid transparent;border-radius:3px}.collapsed .open-bracket{padding-right:0}.td.key>.open-bracket:first-child{margin-left:-2px}.open-bracket:hover{color:var(--primary-color);background-color:var(--hover-color);border:1px solid var(--border-color)}.close-bracket{display:inline-block;font-family:var(--font-mono)}.inside-bracket-wrapper{overflow:hidden}.tree:not(.interactive) .inside-bracket-wrapper{animation-duration:0s!important}.tr:not(.collapsed)+.inside-bracket-wrapper{animation:linear .2s expand-height}.tr.collapsed+.inside-bracket-wrapper{animation:linear .2s collapse-height;max-height:0}.inside-bracket.array,.inside-bracket.object{border-left:1px dotted var(--border-color)}.inside-bracket.xxx-of.option{border-left:1px solid transparent}`];
}
/* eslint-disable indent */
render() {
var _this$data, _this$data2, _this$data3, _this$data3$Props, _this$data4, _this$data4$Props, _this$data5;
const title = ((_this$data = this.data) === null || _this$data === void 0 ? void 0 : _this$data['::title']) || ((_this$data2 = this.data) === null || _this$data2 === void 0 ? void 0 : _this$data2['::type']) === 'array' && ((_this$data3 = this.data) === null || _this$data3 === void 0 ? void 0 : (_this$data3$Props = _this$data3['::props']) === null || _this$data3$Props === void 0 ? void 0 : _this$data3$Props['::title']) && `[${(_this$data4 = this.data) === null || _this$data4 === void 0 ? void 0 : (_this$data4$Props = _this$data4['::props']) === null || _this$data4$Props === void 0 ? void 0 : _this$data4$Props['::title']}]`;
const displayLine = [title, (_this$data5 = this.data) === null || _this$data5 === void 0 ? void 0 : _this$data5['::description']].filter(d => d).join(' - ');
return (0, _lit.html)` <div class="tree ${this.interactive ? 'interactive' : ''}"> <div class="toolbar"> ${displayLine ? (0, _lit.html)`<span class="m-markdown" style="margin-block-start:0"> ${(0, _unsafeHtml.unsafeHTML)((0, _commonUtils.toMarkdown)(displayLine))}</span>` : (0, _lit.html)`<div> </div>`} <div class="toolbar-item" ="${() => this.toggleSchemaDescription()}"> ${this.schemaDescriptionExpanded ? (0, _index.getI18nText)('schemas.collapse-desc') : (0, _index.getI18nText)('schemas.expand-desc')} </div> </div> ${this.data ? (0, _lit.html)`${this.generateTree(this.data['::type'] === 'array' ? this.data['::props'] : this.data, this.data['::type'], this.data['::array-type'] || '')}` : (0, _lit.html)`<span class="mono-font" style="color:var(--red)"> ${(0, _index.getI18nText)('schemas.schema-missing')} </span>`} </div> `;
}
toggleSchemaDescription() {
this.schemaDescriptionExpanded = !this.schemaDescriptionExpanded;
this.requestUpdate();
}
generateTree(data, dataType = 'object', arrayType = '', flags = {}, key = '', title = '', description = '', schemaLevel = 0, indentLevel = 0) {
if (!data) {
return (0, _lit.html)`<div class="null" style="display:inline"> <span class="key-label xxx-of-key"> ${key.replace('::OPTION~', '')}</span> ${dataType === 'array' && (0, _lit.html)`<span class="mono-font"> [ ] </span>` || dataType === 'object' && (0, _lit.html)`<span class="mono-font"> { } </span>` || (0, _lit.html)`<span class="mono-font"> ${dataType} </span>`} </div>`;
}
if (Object.keys(data).length === 0) {
return (0, _lit.html)`<span class="key object">${key}:{ }</span>`;
}
let keyLabel = '';
let keyDescr = '';
if (key.startsWith('::ONE~OF') || key.startsWith('::ANY~OF')) {
keyLabel = key.replace('::', '').replace('~', ' ');
} else if (key.startsWith('::OPTION')) {
const parts = key.split('~');
keyLabel = parts[1];
keyDescr = parts[2];
} else {
keyLabel = key;
}
const leftPadding = 16;
// Min-width used for model keys: `td key `
const minFieldColWidth = 300 - indentLevel * leftPadding;
let openBracket = '';
let closeBracket = '';
const newSchemaLevel = data['::type'] === 'xxx-of-option' ? schemaLevel : schemaLevel + 1;
const newIndentLevel = indentLevel + 1;
if (data['::type'] === 'array') {
if (dataType === 'array') {
const arrType = arrayType !== 'object' ? arrayType : '';
if (schemaLevel < this.schemaExpandLevel) {
openBracket = (0, _lit.html)`<span class="open-bracket array-of-array" data-array-type="${arrType}" ="${this.toggleObjectExpand}">[[ ${arrType} </span>`;
} else {
openBracket = (0, _lit.html)`<span class="open-bracket array-of-array" data-array-type="${arrType}" ="${this.toggleObjectExpand}">[[...]]</span>`;
}
closeBracket = ']]';
} else {
if (schemaLevel < this.schemaExpandLevel) {
openBracket = (0, _lit.html)`<span class="open-bracket array" ="${this.toggleObjectExpand}">[</span>`;
} else {
openBracket = (0, _lit.html)`<span class="open-bracket array" ="${this.toggleObjectExpand}">[...]</span>`;
}
closeBracket = ']';
}
} else if (data['::type'] === 'xxx-of-option') {
if (dataType === 'array') {
if (schemaLevel < this.schemaExpandLevel) {
openBracket = (0, _lit.html)`<span class="open-bracket array" ="${this.toggleObjectExpand}">[</span>`;
} else {
openBracket = (0, _lit.html)`<span class="open-bracket array" ="${this.toggleObjectExpand}">[...]</span>`;
}
closeBracket = ']';
}
} else if (data['::type']) {
if (dataType === 'array') {
if (schemaLevel < this.schemaExpandLevel) {
openBracket = (0, _lit.html)`<span class="open-bracket array-of-object" ="${this.toggleObjectExpand}">[{</span>`;
} else {
openBracket = (0, _lit.html)`<span class="open-bracket array-of-object" ="${this.toggleObjectExpand}">[{...}]</span>`;
}
closeBracket = '}]';
} else {
if (schemaLevel < this.schemaExpandLevel) {
openBracket = (0, _lit.html)`<span class="open-bracket object" ="${this.toggleObjectExpand}">{</span>`;
} else {
openBracket = (0, _lit.html)`<span class="open-bracket object" ="${this.toggleObjectExpand}">{...}</span>`;
}
closeBracket = '}';
}
}
if (typeof data === 'object') {
var _data$Metadata, _data$Metadata$constr;
if (flags['🆁'] && this.schemaHideReadOnly === 'true') {
return undefined;
}
if (flags['🆆'] && this.schemaHideWriteOnly === 'true') {
return undefined;
}
const displayLine = [flags['🆁'] || flags['🆆'], title && `**${title}${description ? ':' : ''}**`, description].filter(v => v).join(' ');
return (0, _lit.html)` <div class="tr ${schemaLevel < this.schemaExpandLevel || data['::type'] && data['::type'].startsWith('xxx-of') ? '' : 'collapsed'} ${data['::type'] || 'no-type-info'}"> <div class="td key ${data['::deprecated'] ? 'deprecated' : ''}" style="min-width:${minFieldColWidth}px"> ${data['::type'] === 'xxx-of-option' || key.startsWith('::OPTION') ? (0, _lit.html)`<span class="key-label xxx-of-key">${keyLabel}</span><span class="xxx-of-descr">${keyDescr}</span>` : keyLabel === '::props' || keyLabel === '::ARRAY~OF' ? '' : schemaLevel > 0 ? (0, _lit.html)`<span class="key-label"> ${keyLabel.replace(/\*$/, '')}${keyLabel.endsWith('*') ? (0, _lit.html)`<span class="requiredStar" title="Required"></span>` : ''}: </span>` : ''} ${openBracket} </div> <div class="td key-descr"> <span class="m-markdown-small" style="vertical-align:middle" title="${flags['🆁'] && 'Read only attribute' || flags['🆆'] && 'Write only attribute' || ''}"> ${(0, _unsafeHtml.unsafeHTML)((0, _commonUtils.toMarkdown)(displayLine))} </span> ${this.schemaDescriptionExpanded ? (0, _lit.html)` ${(_data$Metadata = data['::metadata']) !== null && _data$Metadata !== void 0 && (_data$Metadata$constr = _data$Metadata.constraints) !== null && _data$Metadata$constr !== void 0 && _data$Metadata$constr.length ? (0, _lit.html)`<div style="display:inline-block;line-break:anywhere;margin-right:8px"><span class="bold-text">Constraints: </span>${data['::metadata'].constraints.join(', ')}</div><br>` : ''}` : ''} </div> </div> <div class="inside-bracket-wrapper"> <div class="inside-bracket ${data['::type'] || 'no-type-info'}" style="padding-left:${data['::type'] === 'xxx-of-option' ? 0 : leftPadding}px"> ${Array.isArray(data) && data[0] ? (0, _lit.html)`${this.generateTree(data[0], 'xxx-of-option', '', data[0]['::flags'] || {}, '::ARRAY~OF', data[0]['::title'], data[0]['::description'], newSchemaLevel, newIndentLevel)}` : (0, _lit.html)` ${Object.keys(data).map(dataKey => {
var _data$dataKey;
return !['::metadata', '::title', '::description', '::type', '::link', '::props', '::deprecated', '::array-type', '::dataTypeLabel', '::flags'].includes(dataKey) || (_data$dataKey = data[dataKey]) !== null && _data$dataKey !== void 0 && _data$dataKey['::type'] && !data[dataKey]['::type'].includes('xxx-of') ? (0, _lit.html)`${this.generateTree(data[dataKey]['::type'] === 'array' ? data[dataKey]['::props'] : data[dataKey], data[dataKey]['::type'], data[dataKey]['::array-type'] || '', data[dataKey]['::flags'], dataKey, data[dataKey]['::title'], data[dataKey]['::description'], newSchemaLevel, newIndentLevel)}` : '';
})}`} </div> ${data['::type'] && data['::type'].includes('xxx-of') ? '' : (0, _lit.html)`<div class="close-bracket"> ${closeBracket} </div>`} </div> `;
}
// For Primitive Data types
const {
type,
cssType,
format,
readOrWriteOnly,
constraints,
defaultValue,
example,
allowedValues,
pattern,
schemaDescription,
schemaTitle,
deprecated
} = JSON.parse(data);
if (readOrWriteOnly === '🆁' && this.schemaHideReadOnly === 'true') {
return undefined;
}
if (readOrWriteOnly === '🆆' && this.schemaHideWriteOnly === 'true') {
return undefined;
}
const titleString = schemaTitle || title;
const descriptionString = schemaDescription || description;
return (0, _lit.html)` <div class="tr"> <div class="td key ${deprecated ? 'deprecated' : ''}" style="min-width:${minFieldColWidth}px"> ${keyLabel.endsWith('*') ? (0, _lit.html)`<span class="key-label requiredStar" title="Required">${keyLabel.substring(0, keyLabel.length - 1)}</span>:` : key.startsWith('::OPTION') ? (0, _lit.html)`<span class="key-label xxx-of-key">${keyLabel}</span><span class="xxx-of-descr">${keyDescr}</span>` : schemaLevel > 0 ? (0, _lit.html)`<span class="key-label">${keyLabel}:</span>` : ''} <span>${dataType === 'array' ? '[' : ''}<span class="${cssType}">${format || type}</span>${dataType === 'array' ? ']' : ''}</span> </div> <div class="td key-descr"> <span class="m-markdown-small" style="vertical-align:middle" title="${readOrWriteOnly === '🆁' && 'Read only attribute' || readOrWriteOnly === '🆆' && 'Write only attribute' || ''}"> ${(0, _unsafeHtml.unsafeHTML)((0, _commonUtils.toMarkdown)(`${readOrWriteOnly && `${readOrWriteOnly} ` || ''}${`${titleString ? `**${titleString}${descriptionString ? ':' : ''}**` : ''} ${descriptionString}` || ''}`))} </span> ${this.schemaDescriptionExpanded ? (0, _lit.html)` ${constraints.length ? (0, _lit.html)`<div style="display:inline-block;line-break:anywhere;margin-right:8px"><span class="bold-text">Constraints: </span>${constraints.join(', ')}</div><br>` : ''} ${defaultValue !== '' ? (0, _lit.html)`<div style="display:inline-block;line-break:anywhere;margin-right:8px"><span class="bold-text">Default: </span>${defaultValue}</div><br>` : ''} ${allowedValues ? (0, _lit.html)`<div style="display:inline-block;line-break:anywhere;margin-right:8px"><span class="bold-text">Allowed: </span>${allowedValues.filter(v => v !== null && v !== undefined).join(' ┃ ')}</div><br>` : ''} ${pattern ? (0, _lit.html)`<div style="display:inline-block;line-break:anywhere;margin-right:8px"><span class="bold-text">Pattern: </span>${pattern}</div><br>` : ''} ${example ? (0, _lit.html)`<div style="display:inline-block;line-break:anywhere;margin-right:8px"><span class="bold-text">Example: </span>${example}</div><br>` : ''}` : ''} </div> </div> `;
}
/* eslint-enable indent */
toggleObjectExpand(e) {
const rowEl = e.target.closest('.tr');
rowEl.classList.toggle('collapsed');
if (rowEl.classList.contains('collapsed')) {
e.target.innerHTML = e.target.classList.contains('array-of-object') ? '[{...}]' : e.target.classList.contains('array-of-array') ? '[[...]]' : e.target.classList.contains('array') ? '[...]' : '{...}';
} else {
e.target.innerHTML = e.target.classList.contains('array-of-object') ? '[{' : e.target.classList.contains('array-of-array') ? '[[' : e.target.classList.contains('object') ? '{' : '[';
}
this.requestUpdate();
}
}
exports.default = SchemaTree;
if (!customElements.get('openapi-explorer')) {
customElements.define('schema-tree', SchemaTree);
}