nxt-json-view
Version:
Angular component for interactive json view.
229 lines (221 loc) • 16.5 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, input, model, signal, effect, computed, inject, ViewEncapsulation, Component, linkedSignal, NgModule } from '@angular/core';
class ExpanderService {
constructor() {
this.items = new Set();
}
addItem(item) {
this.items.add(item);
}
removeItem(item) {
this.items.delete(item);
}
expandTo(level) {
this.items.forEach(i => {
if (i.level() <= level && !i.isOpen()) {
i.levelOpen.set(level);
i.toggle();
}
});
}
collapseTo(level) {
this.items.forEach(i => {
if (i.level() > level && i.isOpen())
i.toggle();
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ExpanderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ExpanderService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ExpanderService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
/** @internal */
function isObject(value) {
return value !== null && typeof value === 'object';
}
/** @internal */
function isArray(value) {
return Array.isArray(value);
}
/** @internal */
function isUndefined(value) {
return typeof value === 'undefined';
}
/** @internal */
function isString(value) {
return typeof value === 'string';
}
/** @internal */
function isNumber(value) {
return typeof value === 'number';
}
/** @internal */
function isBoolean(value) {
return typeof value === 'boolean';
}
/** @internal */
class JsonViewItemComponent {
constructor() {
this.data = input(...(ngDevMode ? [undefined, { debugName: "data" }] : []));
this.key = input(...(ngDevMode ? [undefined, { debugName: "key" }] : []));
this.level = input(0, ...(ngDevMode ? [{ debugName: "level" }] : []));
this.levelOpen = model(...(ngDevMode ? [undefined, { debugName: "levelOpen" }] : []));
this.levelLabels = input(...(ngDevMode ? [undefined, { debugName: "levelLabels" }] : []));
this.isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
this.effectRef = effect(() => {
const levelOpen = this.levelOpen();
this.isOpen.set(!isUndefined(levelOpen) && (this.level() <= levelOpen));
this.effectRef.destroy();
}, { ...(ngDevMode ? { debugName: "effectRef" } : {}), manualCleanup: true });
this.childrenKeys = computed(() => {
const data = this.data();
if (isObject(data)) {
return Object.keys(data);
}
return [];
}, ...(ngDevMode ? [{ debugName: "childrenKeys" }] : []));
this.hasChildren = computed(() => !!(this.childrenKeys().length), ...(ngDevMode ? [{ debugName: "hasChildren" }] : []));
this._levelLabels = computed(() => {
const levelLabels = this.levelLabels();
return levelLabels?.[this.level()] || {};
}, ...(ngDevMode ? [{ debugName: "_levelLabels" }] : []));
this.dataType = computed(() => {
let dataType;
const data = this.data();
if (isObject(data)) {
dataType = 'Object';
if (isArray(data)) {
dataType = 'Array';
}
const key = this.key();
if (key && this._levelLabels()[key]) {
dataType = this._levelLabels()[key];
}
}
return dataType;
}, ...(ngDevMode ? [{ debugName: "dataType" }] : []));
this.value = computed(() => {
let value;
const data = this.data();
if (!isObject(data)) {
value = data;
if (null === data) {
value = 'null';
}
}
return value;
}, ...(ngDevMode ? [{ debugName: "value" }] : []));
this.valueType = computed(() => {
const data = this.data();
if (!isObject(data)) {
if (isString(data)) {
return 'string';
}
else if (isNumber(data)) {
return 'number';
}
else if (isBoolean(data)) {
return 'boolean';
}
else if (null === data) {
return 'null';
}
}
return;
}, ...(ngDevMode ? [{ debugName: "valueType" }] : []));
this.isObject = computed(() => isObject(this.data()), ...(ngDevMode ? [{ debugName: "isObject" }] : []));
this.isArray = computed(() => isObject(this.data()) && isArray(this.data()), ...(ngDevMode ? [{ debugName: "isArray" }] : []));
this.expanderService = inject(ExpanderService);
}
ngOnInit() {
this.expanderService.addItem(this);
}
ngOnDestroy() {
this.expanderService.removeItem(this);
}
toggle() {
if (!(this.childrenKeys().length)) {
return;
}
this.isOpen.set(!this.isOpen());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: JsonViewItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: JsonViewItemComponent, isStandalone: true, selector: "nxt-json-view-item", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, levelOpen: { classPropertyName: "levelOpen", publicName: "levelOpen", isSignal: true, isRequired: false, transformFunction: null }, levelLabels: { classPropertyName: "levelLabels", publicName: "levelLabels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { levelOpen: "levelOpenChange" }, ngImport: i0, template: "<div class=\"json-view\">\r\n <a (click)=\"toggle()\"\r\n tabindex=\"0\"\r\n (keydown.space)=\"toggle()\">\r\n <span class=\"toggler\"\r\n [class]=\"{'collapse': isOpen(), 'expand': !isOpen()}\"\r\n [style.visibility]=\"hasChildren()?'visible':'hidden'\"></span>\r\n @if (key()) {\r\n <span class=\"key\">\r\n <span>{{key()}}</span>\r\n <span>: </span>\r\n </span>\r\n }\r\n <span class=\"value\">\r\n @if (isObject()) {\r\n <span>\r\n {{ dataType() }}\r\n @if (isArray()) {\r\n <span>[ {{ $any(data())?.length }} ]</span>\r\n }\r\n </span>\r\n }\r\n @if (!isObject() && valueType()) {\r\n <span [class]=\"valueType()\">{{ value() }}</span>\r\n }\r\n </span>\r\n </a>\r\n @if (hasChildren() && isOpen()) {\r\n <div class=\"children\">\r\n @for (_key of childrenKeys(); track _key) {\r\n <nxt-json-view-item [data]=\"data()[_key]\"\r\n [key]=\"_key\"\r\n [level]=\"level()+1\"\r\n [levelOpen]=\"levelOpen()\"\r\n [levelLabels]=\"levelLabels()\" />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".json-view a{cursor:pointer;text-decoration:none}.json-view .toggler{width:1em;padding:0;display:block;text-align:center;float:left}.json-view .collapse:before{content:\"-\"}.json-view .expand:before{content:\"+\"}.json-view .key{color:var(--nxt-json-view-color-key, #92278f)}.json-view .value{color:var(--nxt-json-view-color-value, #000000)}.json-view .value .string{color:var(--nxt-json-view-color-string, #3ab54a)}.json-view .value .number{color:var(--nxt-json-view-color-number, #25aae2)}.json-view .value .boolean{color:var(--nxt-json-view-color-boolean, #aa0d91)}.json-view .value .null{color:var(--nxt-json-view-color-null, #f1592a)}.json-view .children{margin-left:1em}\n"], dependencies: [{ kind: "component", type: JsonViewItemComponent, selector: "nxt-json-view-item", inputs: ["data", "key", "level", "levelOpen", "levelLabels"], outputs: ["levelOpenChange"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: JsonViewItemComponent, decorators: [{
type: Component,
args: [{ selector: 'nxt-json-view-item', encapsulation: ViewEncapsulation.Emulated, template: "<div class=\"json-view\">\r\n <a (click)=\"toggle()\"\r\n tabindex=\"0\"\r\n (keydown.space)=\"toggle()\">\r\n <span class=\"toggler\"\r\n [class]=\"{'collapse': isOpen(), 'expand': !isOpen()}\"\r\n [style.visibility]=\"hasChildren()?'visible':'hidden'\"></span>\r\n @if (key()) {\r\n <span class=\"key\">\r\n <span>{{key()}}</span>\r\n <span>: </span>\r\n </span>\r\n }\r\n <span class=\"value\">\r\n @if (isObject()) {\r\n <span>\r\n {{ dataType() }}\r\n @if (isArray()) {\r\n <span>[ {{ $any(data())?.length }} ]</span>\r\n }\r\n </span>\r\n }\r\n @if (!isObject() && valueType()) {\r\n <span [class]=\"valueType()\">{{ value() }}</span>\r\n }\r\n </span>\r\n </a>\r\n @if (hasChildren() && isOpen()) {\r\n <div class=\"children\">\r\n @for (_key of childrenKeys(); track _key) {\r\n <nxt-json-view-item [data]=\"data()[_key]\"\r\n [key]=\"_key\"\r\n [level]=\"level()+1\"\r\n [levelOpen]=\"levelOpen()\"\r\n [levelLabels]=\"levelLabels()\" />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".json-view a{cursor:pointer;text-decoration:none}.json-view .toggler{width:1em;padding:0;display:block;text-align:center;float:left}.json-view .collapse:before{content:\"-\"}.json-view .expand:before{content:\"+\"}.json-view .key{color:var(--nxt-json-view-color-key, #92278f)}.json-view .value{color:var(--nxt-json-view-color-value, #000000)}.json-view .value .string{color:var(--nxt-json-view-color-string, #3ab54a)}.json-view .value .number{color:var(--nxt-json-view-color-number, #25aae2)}.json-view .value .boolean{color:var(--nxt-json-view-color-boolean, #aa0d91)}.json-view .value .null{color:var(--nxt-json-view-color-null, #f1592a)}.json-view .children{margin-left:1em}\n"] }]
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], levelOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "levelOpen", required: false }] }, { type: i0.Output, args: ["levelOpenChange"] }], levelLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "levelLabels", required: false }] }] } });
class JsonViewComponent {
constructor() {
/** JSON data, any valid JSON object */
this.data = input(...(ngDevMode ? [undefined, { debugName: "data" }] : []));
/** Initial number of levels to be expanded; to expand / collapse the view after initial render, use `expandTo` / `collapseTo` */
this.levelOpen = input(...(ngDevMode ? [undefined, { debugName: "levelOpen" }] : []));
/** Custom labels for elements within the hierarcy */
this.levelLabels = input(...(ngDevMode ? [undefined, { debugName: "levelLabels" }] : []));
/** @internal */
this._levelOpen = linkedSignal({ ...(ngDevMode ? { debugName: "_levelOpen" } : {}), source: () => this.levelOpen(),
computation: level => level });
this.expanderService = inject(ExpanderService);
}
ngAfterViewChecked() {
if (this._levelOpen() != undefined) {
// Reset levelOpen to keep nested items collapsed when toggling
this._levelOpen.set(undefined);
}
}
/** Expand all items up to the given level */
expandTo(level) {
this._levelOpen.set(level);
this.expanderService.expandTo(level);
}
/** Collapse any open items above the given level */
collapseTo(level) {
this.expanderService.collapseTo(level);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: JsonViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.0", type: JsonViewComponent, isStandalone: true, selector: "nxt-json-view", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, levelOpen: { classPropertyName: "levelOpen", publicName: "levelOpen", isSignal: true, isRequired: false, transformFunction: null }, levelLabels: { classPropertyName: "levelLabels", publicName: "levelLabels", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
{
provide: ExpanderService,
useClass: ExpanderService
}
], ngImport: i0, template: "<div class=\"nxt-json-view\">\r\n <nxt-json-view-item [data]=\"data()\"\r\n [level]=\"0\"\r\n [levelOpen]=\"_levelOpen()\"\r\n [levelLabels]=\"levelLabels()\"\r\n class=\"json-view\" />\r\n</div>\r\n", styles: [""], dependencies: [{ kind: "component", type: JsonViewItemComponent, selector: "nxt-json-view-item", inputs: ["data", "key", "level", "levelOpen", "levelLabels"], outputs: ["levelOpenChange"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: JsonViewComponent, decorators: [{
type: Component,
args: [{ selector: 'nxt-json-view', encapsulation: ViewEncapsulation.Emulated, providers: [
{
provide: ExpanderService,
useClass: ExpanderService
}
], imports: [
JsonViewItemComponent
], template: "<div class=\"nxt-json-view\">\r\n <nxt-json-view-item [data]=\"data()\"\r\n [level]=\"0\"\r\n [levelOpen]=\"_levelOpen()\"\r\n [levelLabels]=\"levelLabels()\"\r\n class=\"json-view\" />\r\n</div>\r\n" }]
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], levelOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "levelOpen", required: false }] }], levelLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "levelLabels", required: false }] }] } });
/** @deprecated use standalone imports */
class JsonViewModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: JsonViewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.0", ngImport: i0, type: JsonViewModule, imports: [JsonViewComponent], exports: [JsonViewComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: JsonViewModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: JsonViewModule, decorators: [{
type: NgModule,
args: [{
imports: [
JsonViewComponent
],
exports: [
JsonViewComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { JsonViewComponent, JsonViewModule };
//# sourceMappingURL=nxt-json-view.mjs.map