Browse Source

WIP: json-viewer

dev
hechang27-sprt 3 months ago
parent
commit
4be6b90acb
  1. 1
      examples/App.vue
  2. 2
      examples/config/language/en.ts
  3. 1
      examples/config/language/zh.ts
  4. 6
      examples/config/router.ts
  5. 361
      examples/view/base/json-view.vue
  6. 452
      packages/base/data/json-view/flattenJson.ts
  7. 17
      packages/base/data/json-view/index.ts
  8. 1074
      packages/base/data/json-view/json-view.vue
  9. 95
      packages/base/data/json-view/types.ts
  10. 3
      packages/base/index.ts

1
examples/App.vue

@ -43,6 +43,7 @@ const menus = [ @@ -43,6 +43,7 @@ const menus = [
children: [
{ i18n: "menu.table", path: "table", icon: "List" },
{ i18n: "menu.tableV2", path: "table-v2", icon: "List" },
{ i18n: "menu.jsonView", path: "json-view", icon: "Document" },
{ i18n: "menu.form", path: "form", icon: "Postcard" },
{ i18n: "menu.pretextDemo", path: "pretext-demo", icon: "List" },
],

2
examples/config/language/en.ts

@ -37,6 +37,8 @@ export default class En extends Lang.En { @@ -37,6 +37,8 @@ export default class En extends Lang.En {
base: 'General',
table: 'Table',
tableV2: 'Table(V2)',
jsonView: 'JSON View',
pretextDemo: 'Pretext Demo',
form: 'Form',
tool: 'Tool',
terminal: 'Terminal',

1
examples/config/language/zh.ts

@ -30,6 +30,7 @@ export default class Zh extends Lang.Zh { @@ -30,6 +30,7 @@ export default class Zh extends Lang.Zh {
base: "通用",
table: "表格",
tableV2: "表格(V2)",
jsonView: "JSON 视图",
pretextDemo: "Pretext Demo",
form: "表单",
tool: "工具",

6
examples/config/router.ts

@ -3,6 +3,7 @@ import { Views, Common } from "noob-mengyxu"; @@ -3,6 +3,7 @@ import { Views, Common } from "noob-mengyxu";
import Home from "../view/home.vue";
import Table from "../view/base/table.vue";
import TableV2 from "../view/base/table-v2.vue";
import JsonViewDemo from "../view/base/json-view.vue";
import Form from "../view/base/form.vue";
import Terminal from "../view/tool/terminal.vue";
import Color from "../view/tool/color.vue";
@ -33,6 +34,11 @@ const routes: Array<RouteRecordRaw> = [ @@ -33,6 +34,11 @@ const routes: Array<RouteRecordRaw> = [
name: "table-v2",
component: TableV2,
},
{
path: "/json-view",
name: "json-view",
component: JsonViewDemo,
},
{
path: "/form",
name: "form",

361
examples/view/base/json-view.vue

@ -0,0 +1,361 @@ @@ -0,0 +1,361 @@
<template>
<div class="json-view-demo">
<section class="demo-panel">
<div class="demo-panel__header">
<div>
<h2>JSON View</h2>
<p>Read-only JSON tree with collapse controls, virtualization, and low-node CSS-based indentation guides.</p>
</div>
</div>
<div class="demo-grid">
<div class="demo-card textarea">
<h3>Source</h3>
<textarea v-model="source" class="demo-source"></textarea>
<p v-if="parseError" class="demo-error">{{ parseError }}</p>
</div>
<div class="demo-card options">
<h3>Options</h3>
<label class="demo-option">
<span>Virtual</span>
<input v-model="options.virtual" type="checkbox" />
</label>
<label class="demo-option">
<span>Dynamic height</span>
<input v-model="options.dynamicHeight" type="checkbox" />
</label>
<label class="demo-option">
<span>Show line guides</span>
<input v-model="options.showLine" type="checkbox" />
</label>
<label class="demo-option">
<span>Show line numbers</span>
<input v-model="options.showLineNumber" type="checkbox" />
</label>
<label class="demo-option">
<span>Show toggles</span>
<input v-model="options.showIcon" type="checkbox" />
</label>
<label class="demo-option">
<span>Show collapsed length</span>
<input v-model="options.showLength" type="checkbox" />
</label>
<label class="demo-option">
<span>Theme</span>
<select v-model="options.theme">
<option value="light">light</option>
<option value="dark">dark</option>
</select>
</label>
<label class="demo-option">
<span>Indent</span>
<input v-model.number="options.indent" min="1" max="6" type="number" />
</label>
<label class="demo-option">
<span>Height</span>
<input v-model.number="options.height" min="180" max="720" step="20" type="number" />
</label>
<label class="demo-option">
<span>Item height</span>
<input v-model.number="options.itemHeight" min="18" max="48" step="2" type="number" />
</label>
<label class="demo-option">
<span>Collapse deeper than</span>
<input v-model.number="options.deep" min="1" max="8" type="number" />
</label>
<label class="demo-option">
<span>Collapse length over</span>
<input v-model.number="options.collapsedNodeLength" min="1" max="40" type="number" />
</label>
</div>
</div>
</section>
<section class="demo-panel">
<div class="demo-panel__header">
<div>
<h3>Parsed Input</h3>
<p>Primary viewer using the current source text, including the built-in copy menu and one custom item.</p>
</div>
</div>
<JsonView
:data="parsedData"
:virtual="options.virtual"
:dynamic-height="options.dynamicHeight"
:show-line="options.showLine"
:show-line-number="options.showLineNumber"
:show-icon="options.showIcon"
:show-length="options.showLength"
:theme="options.theme"
:indent="options.indent"
:height="options.height"
:item-height="options.itemHeight"
:deep="normalizeNumber(options.deep)"
:collapsed-node-length="normalizeNumber(options.collapsedNodeLength)"
:menu-items="menuItems"
/>
</section>
<section class="demo-panel">
<div class="demo-panel__header">
<div>
<h3>Large Virtualized Payload</h3>
<p>Stress case with a large array to exercise flattening and virtualization.</p>
</div>
</div>
<JsonView
:data="largeData"
:virtual="true"
:dynamic-height="false"
:show-line="true"
:show-line-number="true"
:show-icon="true"
:show-length="true"
theme="dark"
:indent="2"
:height="420"
:item-height="22"
:collapsed-node-length="18"
/>
</section>
<section class="demo-panel">
<div class="demo-panel__header">
<div>
<h3>JS Collections And Cycles</h3>
<p>Verifies support for `Map`, `Set`, and cyclic references that are not representable in JSON text.</p>
</div>
</div>
<JsonView
:data="jsData"
:virtual="true"
:dynamic-height="true"
:show-line="true"
:show-line-number="true"
:show-icon="true"
:show-length="true"
theme="light"
:indent="2"
:height="320"
:item-height="20"
/>
</section>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from "vue";
import { JsonView } from "noob-mengyxu";
const initialData = {
id: "case-1024",
owner: {
id: 7,
name: "Avery Stone",
roles: ["admin", "ops", "audit"],
},
metrics: {
total: 1532,
successRate: 0.9821,
active: true,
notes: null,
},
timeline: [
{ at: 1710000000, event: "created" },
{ at: 1710003600, event: "validated" },
{ at: 1710007200, event: "published" },
],
payload: {
summary: "This is a longer string intended to demonstrate optional dynamic row height handling in the viewer.",
tags: ["alpha", "beta", "gamma"],
nested: {
one: { two: { three: { four: "deep value" } } },
mixed: [1, "two", false, null, { end: true }],
},
},
};
const source = ref(JSON.stringify(initialData, null, 2));
const parseError = ref("");
const options = reactive({
virtual: true,
dynamicHeight: true,
showLine: true,
showLineNumber: false,
showIcon: true,
showLength: true,
theme: "light" as "light" | "dark",
indent: 2,
height: 360,
itemHeight: 20,
deep: 4,
collapsedNodeLength: 10,
});
const parsedData = computed(() => {
try {
parseError.value = "";
return JSON.parse(source.value);
} catch (error) {
parseError.value = error instanceof Error ? error.message : String(error);
return initialData;
}
});
const largeData = computed(() => ({
meta: {
generatedAt: "2026-04-09T11:30:00Z",
totalRows: 1500,
},
items: Array.from({ length: 1500 }, (_, index) => ({
id: index + 1,
group: `batch-${Math.floor(index / 50)}`,
active: index % 3 === 0,
score: Number((Math.sin(index / 10) * 100).toFixed(3)),
tags: [`tag-${index % 5}`, `tag-${index % 7}`, `tag-${index % 11}`],
details: {
owner: `user-${index % 23}`,
region: ["us", "eu", "apac"][index % 3],
comment: `Item ${
index + 1
} contains enough text to keep the viewer honest about large arrays and scrolling behavior.`,
},
})),
}));
const jsData = computed(() => {
const owner = {
id: 7,
name: "Avery Stone",
};
const cycleRoot: Record<string, unknown> = {
id: "cyclic-root",
owner,
};
const linked = {
parent: cycleRoot,
owner,
};
cycleRoot.self = cycleRoot;
cycleRoot.linked = linked;
return {
owner,
pairMap: new Map<unknown, unknown>([
["owner", owner],
[42, { status: "ok" }],
[{ kind: "object-key" }, new Set(["alpha", "beta"])],
]),
valueSet: new Set<unknown>(["alpha", 42, owner, cycleRoot]),
cycleRoot,
};
});
const menuItems = ({ value, copy }: { value: unknown; copy: (text: string) => Promise<boolean> }) => [
{
key: "copy-type",
label: "Copy Type",
onSelect: async () => {
const type =
value === null ? "null" : Array.isArray(value) ? "array" : typeof value === "object" ? "object" : typeof value;
await copy(type);
},
},
];
function normalizeNumber(value: number) {
return Number.isFinite(value) && value > 0 ? value : undefined;
}
</script>
<style lang="scss" scoped>
.json-view-demo {
display: grid;
gap: 20px;
padding: 20px;
}
.demo-panel {
background: #fff;
border: 1px solid #dfe4ea;
border-radius: 14px;
display: grid;
gap: 16px;
padding: 18px;
}
.demo-panel__header h2,
.demo-panel__header h3 {
margin: 0 0 6px;
}
.demo-panel__header p {
color: #5b6472;
margin: 0;
}
.demo-grid {
display: flex;
flex-direction: row;
gap: 16px;
}
.demo-card {
display: flex;
flex-direction: column;
gap: 12px;
}
.demo-card.textarea {
flex: 1;
min-width: 0;
}
.demo-card.options {
/* margin: 20px; */
}
.demo-card h3 {
margin: 0;
}
.demo-source {
border: 1px solid #cbd5e1;
border-radius: 10px;
font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace;
min-height: 260px;
padding: 12px;
resize: vertical;
}
.demo-option {
align-items: center;
display: grid;
gap: 10px;
grid-template-columns: 1fr auto;
}
.demo-option input,
.demo-option select {
min-width: 96px;
}
.demo-error {
color: #b42318;
margin: 0;
}
@media (max-width: 960px) {
.demo-grid {
grid-template-columns: 1fr;
}
}
</style>

452
packages/base/data/json-view/flattenJson.ts

@ -0,0 +1,452 @@ @@ -0,0 +1,452 @@
import type { BuildVisibleJsonRowsOptions, JsonViewNode, JsonViewNodeType } from "./types";
type PlainObject = Record<string, unknown>;
type JsonContainerKind = "object" | "array" | "map" | "set";
type JsonContainerValue = PlainObject | unknown[] | Map<unknown, unknown> | Set<unknown>;
interface RowContext {
path: string;
level: number;
key?: string;
displayKey?: string;
keyValue?: unknown;
index?: number;
showComma: boolean;
posInSet: number;
setSize: number;
}
interface LineCounter {
nextLineNumber: number;
}
function isRecordLike(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set);
}
function buildNodeId(path: string, type: JsonViewNodeType) {
return `${path}:${type}`;
}
function buildObjectPath(parentPath: string, key: string) {
if (/^[A-Za-z_$][\w$]*$/.test(key)) {
return `${parentPath}.${key}`;
}
return `${parentPath}[${JSON.stringify(key)}]`;
}
function buildArrayPath(parentPath: string, index: number) {
return `${parentPath}[${index}]`;
}
function buildMapPath(parentPath: string, key: unknown, index: number) {
if (typeof key === "string") {
return `${parentPath}.get(${JSON.stringify(key)})`;
}
if (typeof key === "number" || typeof key === "boolean" || typeof key === "bigint") {
return `${parentPath}.get(${String(key)})`;
}
if (key === null) {
return `${parentPath}.get(null)`;
}
if (key === undefined) {
return `${parentPath}.get(undefined)`;
}
if (typeof key === "symbol") {
return `${parentPath}.get(${String(key)})`;
}
return `${parentPath}.entries[${index}]`;
}
function isCollapsedByDefault(level: number, length: number, options: BuildVisibleJsonRowsOptions) {
const deepCollapsed = typeof options.deep === "number" ? level + 1 > options.deep : false;
const lengthCollapsed =
typeof options.collapsedNodeLength === "number" ? length > options.collapsedNodeLength : false;
return deepCollapsed || lengthCollapsed;
}
function isExpanded(path: string, level: number, length: number, options: BuildVisibleJsonRowsOptions) {
const explicit = options.expandedState.get(path);
if (explicit !== undefined) {
return explicit;
}
return !isCollapsedByDefault(level, length, options);
}
function formatKeyDisplay(value: unknown) {
if (typeof value === "string") {
return JSON.stringify(value);
}
if (typeof value === "number" || typeof value === "boolean") {
return String(value);
}
if (typeof value === "bigint") {
return `${String(value)}n`;
}
if (value === null) {
return "null";
}
if (value === undefined) {
return "undefined";
}
if (typeof value === "symbol") {
return value.toString();
}
if (typeof value === "function") {
return value.name ? `[Function ${value.name}]` : "[Function]";
}
if (Array.isArray(value)) {
return `Array(${value.length})`;
}
if (value instanceof Map) {
return `Map(${value.size})`;
}
if (value instanceof Set) {
return `Set(${value.size})`;
}
return "Object";
}
function formatPrimitiveDisplay(value: unknown) {
if (typeof value === "string") {
return JSON.stringify(value);
}
if (typeof value === "number" || typeof value === "boolean") {
return String(value);
}
if (typeof value === "bigint") {
return `${String(value)}n`;
}
if (value === null) {
return "null";
}
if (value === undefined) {
return "undefined";
}
if (typeof value === "symbol") {
return value.toString();
}
if (typeof value === "function") {
return value.name ? `[Function ${value.name}]` : "[Function]";
}
return String(value);
}
function getContainerKind(value: unknown): JsonContainerKind | null {
if (Array.isArray(value)) {
return "array";
}
if (value instanceof Map) {
return "map";
}
if (value instanceof Set) {
return "set";
}
if (isRecordLike(value)) {
return "object";
}
return null;
}
function getContainerLength(value: JsonContainerValue, kind: JsonContainerKind) {
switch (kind) {
case "array":
return (value as unknown[]).length;
case "map":
return (value as Map<unknown, unknown>).size;
case "set":
return (value as Set<unknown>).size;
case "object":
default:
return Object.keys(value as PlainObject).length;
}
}
function getContainerContent(kind: JsonContainerKind, state: "start" | "end" | "collapsed") {
switch (kind) {
case "map":
if (state === "start") return "Map {";
if (state === "end") return "}";
return "Map {...}";
case "set":
if (state === "start") return "Set [";
if (state === "end") return "]";
return "Set [...]";
case "object":
if (state === "start") return "{";
if (state === "end") return "}";
return "{...}";
case "array":
default:
if (state === "start") return "[";
if (state === "end") return "]";
return "[...]";
}
}
function getContainerNodeType(kind: JsonContainerKind, state: "start" | "end" | "collapsed"): JsonViewNodeType {
if (kind === "object" || kind === "map") {
if (state === "start") return "objectStart";
if (state === "end") return "objectEnd";
return "objectCollapsed";
}
if (state === "start") return "arrayStart";
if (state === "end") return "arrayEnd";
return "arrayCollapsed";
}
function countValueLines(value: unknown, ancestors = new WeakSet<object>()): number {
const kind = getContainerKind(value);
if (!kind) {
return 1;
}
const reference = value as object;
if (ancestors.has(reference)) {
return 1;
}
ancestors.add(reference);
const total =
kind === "array"
? 2 + (value as unknown[]).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0)
: kind === "set"
? 2 + Array.from(value as Set<unknown>).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0)
: kind === "map"
? 2 +
Array.from((value as Map<unknown, unknown>).values()).reduce<number>(
(sum, entry) => sum + countValueLines(entry, ancestors),
0
)
: 2 +
Object.values(value as PlainObject).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0);
ancestors.delete(reference);
return total;
}
function countContainerChildLines(kind: JsonContainerKind, value: JsonContainerValue, ancestors: WeakSet<object>) {
if (kind === "array") {
return (value as unknown[]).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0);
}
if (kind === "set") {
return Array.from(value as Set<unknown>).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0);
}
if (kind === "map") {
return Array.from((value as Map<unknown, unknown>).values()).reduce<number>(
(sum, entry) => sum + countValueLines(entry, ancestors),
0
);
}
return Object.values(value as PlainObject).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0);
}
function createNode(
counter: LineCounter,
context: RowContext,
type: JsonViewNodeType,
content: string,
value: unknown,
extras?: Partial<JsonViewNode>
): JsonViewNode {
return {
id: buildNodeId(context.path, type),
path: context.path,
lineNumber: counter.nextLineNumber++,
level: context.level,
key: context.key,
displayKey: context.displayKey,
keyValue: context.keyValue,
index: context.index,
content,
showComma: context.showComma,
type,
value,
isLeaf: type === "content",
hasToggle: false,
isExpanded: false,
posInSet: context.posInSet,
setSize: context.setSize,
...extras,
};
}
function appendValueRows(
value: unknown,
context: RowContext,
rows: JsonViewNode[],
options: BuildVisibleJsonRowsOptions,
counter: LineCounter,
ancestors: WeakSet<object>
) {
const kind = getContainerKind(value);
if (!kind) {
rows.push(createNode(counter, context, "content", formatPrimitiveDisplay(value), value, { isLeaf: true }));
return;
}
const reference = value as object;
if (ancestors.has(reference)) {
rows.push(createNode(counter, context, "content", "[Circular]", value, { isLeaf: true }));
return;
}
ancestors.add(reference);
appendContainerRows(kind, value as JsonContainerValue, context, rows, options, counter, ancestors);
ancestors.delete(reference);
}
function appendContainerRows(
kind: JsonContainerKind,
value: JsonContainerValue,
context: RowContext,
rows: JsonViewNode[],
options: BuildVisibleJsonRowsOptions,
counter: LineCounter,
ancestors: WeakSet<object>
) {
const length = getContainerLength(value, kind);
const expanded = isExpanded(context.path, context.level, length, options);
const totalLineCount = 2 + countContainerChildLines(kind, value, ancestors);
if (!expanded) {
rows.push(
createNode(counter, context, getContainerNodeType(kind, "collapsed"), getContainerContent(kind, "collapsed"), value, {
length,
hasToggle: true,
isExpanded: false,
isLeaf: false,
})
);
counter.nextLineNumber += totalLineCount - 1;
return;
}
rows.push(
createNode(counter, context, getContainerNodeType(kind, "start"), getContainerContent(kind, "start"), value, {
length,
hasToggle: true,
isExpanded: true,
isLeaf: false,
showComma: false,
})
);
if (kind === "object") {
Object.entries(value as PlainObject).forEach(([key, childValue], index) => {
appendValueRows(
childValue,
{
path: buildObjectPath(context.path, key),
level: context.level + 1,
key,
showComma: index < length - 1,
posInSet: index + 1,
setSize: length,
},
rows,
options,
counter,
ancestors
);
});
} else if (kind === "array") {
(value as unknown[]).forEach((childValue, index) => {
appendValueRows(
childValue,
{
path: buildArrayPath(context.path, index),
level: context.level + 1,
index,
showComma: index < length - 1,
posInSet: index + 1,
setSize: length,
},
rows,
options,
counter,
ancestors
);
});
} else if (kind === "set") {
Array.from(value as Set<unknown>).forEach((childValue, index) => {
appendValueRows(
childValue,
{
path: buildArrayPath(context.path, index),
level: context.level + 1,
index,
showComma: index < length - 1,
posInSet: index + 1,
setSize: length,
},
rows,
options,
counter,
ancestors
);
});
} else {
Array.from(value as Map<unknown, unknown>).forEach(([keyValue, childValue], index) => {
appendValueRows(
childValue,
{
path: buildMapPath(context.path, keyValue, index),
level: context.level + 1,
displayKey: formatKeyDisplay(keyValue),
keyValue,
showComma: index < length - 1,
posInSet: index + 1,
setSize: length,
},
rows,
options,
counter,
ancestors
);
});
}
rows.push(
createNode(counter, context, getContainerNodeType(kind, "end"), getContainerContent(kind, "end"), value, {
length,
hasToggle: false,
isExpanded: expanded,
isLeaf: false,
key: undefined,
displayKey: undefined,
keyValue: undefined,
index: undefined,
})
);
}
export function buildVisibleJsonRows(value: unknown, options: BuildVisibleJsonRowsOptions) {
const rows: JsonViewNode[] = [];
const counter: LineCounter = { nextLineNumber: 1 };
appendValueRows(
value,
{
path: options.rootPath,
level: 0,
showComma: false,
posInSet: 1,
setSize: 1,
},
rows,
options,
counter,
new WeakSet<object>()
);
return rows;
}

17
packages/base/data/json-view/index.ts

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
import JsonView from "./json-view.vue";
export { JsonView };
export default JsonView;
export { buildVisibleJsonRows } from "./flattenJson";
export type {
BuildVisibleJsonRowsOptions,
JsonViewNode,
JsonViewNodeActionsRenderer,
JsonViewNodeKeyRenderer,
JsonViewNodeRendererArgs,
JsonViewNodeType,
JsonViewNodeValueRenderer,
JsonViewProps,
JsonViewTheme,
} from "./types";

1074
packages/base/data/json-view/json-view.vue

File diff suppressed because it is too large Load Diff

95
packages/base/data/json-view/types.ts

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
import type { VNodeChild } from "vue";
export type JsonViewTheme = "light" | "dark";
export type JsonViewNodeType =
| "content"
| "objectStart"
| "objectEnd"
| "objectCollapsed"
| "arrayStart"
| "arrayEnd"
| "arrayCollapsed";
export interface JsonViewNode {
id: string;
path: string;
lineNumber: number;
level: number;
key?: string;
displayKey?: string;
keyValue?: unknown;
index?: number;
content: string;
length?: number;
showComma: boolean;
type: JsonViewNodeType;
value?: unknown;
isLeaf: boolean;
hasToggle: boolean;
isExpanded: boolean;
posInSet: number;
setSize: number;
}
export interface JsonViewNodeRendererArgs {
node: JsonViewNode;
defaultKey?: string;
defaultValue?: string;
defaultActions?: null;
}
export interface JsonViewMenuActionContext {
node: JsonViewNode;
path: string;
value: unknown;
copy: (text: string) => Promise<boolean>;
closeMenu: () => void;
}
export interface JsonViewMenuItem {
key: string;
label: string;
disabled?: boolean;
onSelect?: (context: JsonViewMenuActionContext) => void | Promise<void>;
}
export type JsonViewMenuItemsResolver =
| JsonViewMenuItem[]
| ((context: JsonViewMenuActionContext) => JsonViewMenuItem[]);
export type JsonViewNodeKeyRenderer = (args: JsonViewNodeRendererArgs) => VNodeChild;
export type JsonViewNodeValueRenderer = (args: JsonViewNodeRendererArgs) => VNodeChild;
export type JsonViewNodeActionsRenderer = (args: JsonViewNodeRendererArgs) => VNodeChild;
export interface JsonViewProps {
data?: unknown;
rootPath?: string;
indent?: number;
collapsedNodeLength?: number;
deep?: number;
showLength?: boolean;
showLine?: boolean;
showLineNumber?: boolean;
showIcon?: boolean;
showDoubleQuotes?: boolean;
showKeyValueSpace?: boolean;
virtual?: boolean;
height?: number;
itemHeight?: number;
dynamicHeight?: boolean;
collapsedOnClickBrackets?: boolean;
theme?: JsonViewTheme;
renderNodeKey?: JsonViewNodeKeyRenderer;
renderNodeValue?: JsonViewNodeValueRenderer;
renderNodeActions?: JsonViewNodeActionsRenderer;
showMenu?: boolean;
menuItems?: JsonViewMenuItemsResolver;
}
export interface BuildVisibleJsonRowsOptions {
rootPath: string;
deep?: number;
collapsedNodeLength?: number;
expandedState: ReadonlyMap<string, boolean>;
}

3
packages/base/index.ts

@ -12,6 +12,7 @@ import WsMonitorToggle from "./item/ws-monitor-toggle.vue"; @@ -12,6 +12,7 @@ import WsMonitorToggle from "./item/ws-monitor-toggle.vue";
import SearchRow from "./data/search-row.vue";
import ListTable from "./data/list-table.vue";
import ListTableV2 from "./data/list-table-v2/list-table-v2.vue";
import JsonView from "./data/json-view/json-view.vue";
import Infomation from "./data/infomation.vue";
import ModifyForm from "./data/modify-form.vue";
import Descriptions from "./data/descriptions.vue";
@ -32,4 +33,4 @@ export { @@ -32,4 +33,4 @@ export {
WsMonitorToggle,
};
export { SearchRow, ListTable, ListTableV2, ListTableDialog, Infomation, ModifyForm, Descriptions, TableAction };
export { SearchRow, ListTable, ListTableV2, JsonView, ListTableDialog, Infomation, ModifyForm, Descriptions, TableAction };

Loading…
Cancel
Save