UNPKG

rapidoc

Version:

RapiDoc - Open API spec viewer with built in console

172 lines (162 loc) • 5.31 kB
import { LitElement, html, css } from "lit"; import { customElement, property } from "lit/decorators.js"; import { copyToClipboard } from "~/utils/common-utils"; import FontStyles from "~/styles/font-styles"; import BorderStyles from "~/styles/border-styles"; import InputStyles from "~/styles/input-styles"; import CustomStyles from "~/styles/custom-styles"; @customElement("json-tree") export default class JsonTree extends LitElement { @property({ type: Object }) data = null; @property({ type: String, attribute: "render-style" }) renderStyle = ""; static get styles() { return [ FontStyles, BorderStyles, InputStyles, css` :host { display: flex; } :where(button, input[type='checkbox'], [tabindex='0']):focus-visible { box-shadow: var(--focus-shadow); } :where(input[type='text'], input[type='password'], select, textarea):focus-visible { border-color: var(--primary); } .json-tree { position: relative; font-family: var(--font-mono); font-size: var(--font-size-small); display: inline-block; overflow: hidden; word-break: break-all; flex: 1; line-height: calc(var(--font-size-small) + 6px); min-height: 40px; direction: ltr; text-align: left; } .open-bracket { display: inline-block; padding: 0 20px 0 0; cursor: pointer; border: 1px solid transparent; border-radius: min(var(--radius), 0.5rem); &:hover { color: var(--primary); border: 1px solid var(--border); } &.expanded:hover ~ .inside-bracket { border-left: 1px solid var(--muted-foreground); } &.expanded:hover ~ .close-bracket { color: var(--primary); } &.collapsed + .inside-bracket, &.collapsed + .inside-bracket + .close-bracket { display: none; } &.expanded .open-bracket-collapsed { display: none; } &.collapsed .open-bracket-expanded { display: none; } } .close-bracket { border: 1px solid transparent; border-radius: min(var(--radius), 0.5rem); display: inline-block; } .inside-bracket { padding-left: 12px; overflow: hidden; border-left: 1px dotted var(--border); } .string { color: var(--green); } .number { color: var(--blue); } .null { color: var(--destructive); } .boolean { color: var(--purple); } .object { color: var(--foreground); } .toolbar { position: absolute; top: 5px; right: 6px; display: flex; padding: 2px; align-items: center; } `, CustomStyles, ]; } render() { return html` <div class="json-tree" @click="${(e) => { if (e.target.classList.contains("btn-copy")) { copyToClipboard(JSON.stringify(this.data, null, 2), e); } else { this.toggleExpand(e); } }}" > <div class="toolbar"> <button class="btn btn-sm btn-outline toolbar-btn btn-copy" part="btn btn-outline btn-copy">Copy</button> </div> ${this.generateTree(this.data, true)} </div> `; } generateTree(data, isLast = false) { if (data === null) { return html`<span class="null">null</span>${isLast ? "" : ","}`; } if (typeof data === "object" && data instanceof Date === false) { const detailType = Array.isArray(data) ? "array" : "pure_object"; if (Object.keys(data).length === 0) { return html`${Array.isArray(data) ? "[ ]," : "{ },"}`; } return html` <div class="open-bracket expanded ${detailType === "array" ? "array" : "object"}"> <span class="open-bracket-expanded">${detailType === "array" ? "[" : "{"}</span> <span class="open-bracket-collapsed">${detailType === "array" ? "[...]" : "{...}"}</span> </div> <div class="inside-bracket"> ${Object.keys(data).map( (key, i, a) => html`<div class="item"> ${detailType === "pure_object" ? html`"${key}":` : ""} ${this.generateTree(data[key], i === a.length - 1)} </div>`, )} </div> <div class="close-bracket">${detailType === "array" ? "]" : "}"}${isLast ? "" : ","}</div> `; } return typeof data === "string" || data instanceof Date ? html`<span class="${typeof data}">"${data}"</span>${isLast ? "" : ","}` : html`<span class="${typeof data}">${data}</span>${isLast ? "" : ","}`; } toggleExpand(e) { const openBracketEl = e.target.closest(".open-bracket"); if (openBracketEl) { if (openBracketEl.classList.contains("expanded")) { openBracketEl.classList.replace("expanded", "collapsed"); } else { openBracketEl.classList.replace("collapsed", "expanded"); } } } }