UNPKG

json-format-tree

Version:

json格式化组件,配合json-source-map实现高亮效果

613 lines (612 loc) 17 kB
import { defineComponent as F, createVNode as d, computed as k, reactive as H, createTextVNode as $, ref as R, watchEffect as Y, watch as I } from "vue"; function E(e) { return Object.prototype.toString.call(e).slice(8, -1).toLowerCase(); } function V(e, n = "", o = 0, u) { const { key: C, index: v, type: m = "content", showComma: s = !1, length: w = 1 } = u || {}, f = E(e); if (f === "array") { const b = A( e.map( (N, g, T) => V(N, `${n}/${g}`, o + 1, { index: g, showComma: g !== T.length - 1, length: w, type: m }) ) ); return [ V("[", n, o, { showComma: !1, key: C, length: e.length, type: "arrayStart" })[0] ].concat( b, V("]", n, o, { showComma: s, length: e.length, type: "arrayEnd" })[0] ); } else if (f === "object") { const b = Object.keys(e), N = A( b.map( (g, T, P) => V( e[g], /^[a-zA-Z_]\w*$/.test(g) ? `${n}/${g}` : `${n}/${g}`, o + 1, { key: g, showComma: T !== P.length - 1, length: w, type: m } ) ) ); return [ V("{", n, o, { showComma: !1, key: C, index: v, length: b.length, type: "objectStart" })[0] ].concat( N, V("}", n, o, { showComma: s, length: b.length, type: "objectEnd" })[0] ); } return [ { content: e, level: o, key: C, index: v, path: n, showComma: s, length: w, type: m } ]; } function A(e) { if (typeof Array.prototype.flat == "function") return e.flat(); const n = [...e], o = []; for (; n.length; ) { const u = n.shift(); Array.isArray(u) ? n.unshift(...u) : o.push(u); } return o; } function O(e, n = /* @__PURE__ */ new WeakMap()) { if (e == null) return e; if (e instanceof Date) return new Date(e); if (e instanceof RegExp) return new RegExp(e); if (typeof e != "object") return e; if (n.get(e)) return n.get(e); if (Array.isArray(e)) { const u = e.map((C) => O(C, n)); return n.set(e, u), u; } const o = {}; for (const u in e) o[u] = O(e[u], n); return n.set(e, o), o; } function q(e) { let n; return e === "null" ? n = null : e === "undefined" ? n = void 0 : e === "true" ? n = !0 : e === "false" ? n = !1 : e[0] + e[e.length - 1] === '""' || e[0] + e[e.length - 1] === "''" ? n = e.slice(1, -1) : typeof Number(e) == "number" && !isNaN(Number(e)) || e === "NaN" ? n = Number(e) : n = e, n; } const z = /* @__PURE__ */ F({ props: { data: { required: !0, type: String }, onClick: Function }, render() { const { data: e } = this, { onClick: n } = this; return d("span", { class: "vjs-tree-brackets", onClick: n }, [e]); } }), J = /* @__PURE__ */ F({ props: { nodeType: { required: !0, type: String }, onClick: Function }, render() { const { nodeType: e } = this, { onClick: n } = this, o = e === "objectStart" || e === "arrayStart"; return !o && !(e === "objectCollapsed" || e === "arrayCollapsed") ? null : d("span", { class: `vjs-carets vjs-carets-${o ? "open" : "close"}`, onClick: n }, [d("svg", { viewBox: "0 0 1024 1024", focusable: "false", "data-icon": "caret-down", width: "1em", height: "1em", fill: "currentColor", "aria-hidden": "true" }, [d("path", { d: "M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z" }, null)])]); } }), K = { // Whether to display the length of (array|object). showLength: { type: Boolean, default: !1 }, // Whether the key name uses double quotes. showDoubleQuotes: { type: Boolean, default: !0 }, // Custom render for key. renderNodeKey: Function, // Custom render for value. renderNodeValue: Function, // Define the selection method supported by the data level, which is not available by default. selectableType: String, // Whether to display the selection control. showSelectController: { type: Boolean, default: !1 }, // Whether to display the data level connection. showLine: { type: Boolean, default: !1 }, showLineNumber: { type: Boolean, default: !1 }, // Whether to trigger selection when clicking on the node. selectOnClickNode: { type: Boolean, default: !0 }, // When using the selectableType, define whether current path/content is enabled. nodeSelectable: { type: Function, default: () => !0 }, // Highlight current node when selected. highlightSelectedNode: { type: Boolean, default: !1 }, showIcon: { type: Boolean, default: !0 }, theme: { type: String, default: "light" }, showKeyValueSpace: { type: Boolean, default: !0 }, editable: { type: Boolean, default: !1 }, editableTrigger: { type: String, default: "click" }, onNodeClick: { type: Function }, onBracketsClick: { type: Function }, onIconClick: { type: Function }, onValueChange: { type: Function } }, U = /* @__PURE__ */ F({ name: "TreeNode", props: { ...K, // Current node data. node: { type: Object, required: !0 }, // Whether the current node is collapsed. collapsed: Boolean, // Whether the current node is checked(When using the selection function). checked: Boolean, style: Object, onSelectedChange: { type: Function }, pointer: { type: Object, default: () => { } } }, emits: ["nodeClick", "bracketsClick", "iconClick", "selectedChange", "valueChange"], setup(e, { emit: n }) { const o = k(() => E(e.node.content)), u = k(() => `vjs-value vjs-value-${o.value}`), C = k(() => e.showDoubleQuotes ? `"${e.node.key}"` : e.node.key), v = () => { const l = e.renderNodeKey; return l ? l({ node: e.node, defaultKey: C.value || "" }) : C.value; }, m = k(() => e.selectableType === "multiple"), s = k(() => e.selectableType === "single"), w = k(() => e.nodeSelectable(e.node) && (m.value || s.value)), f = H({ editing: !1 }), b = (l) => { var t; const y = (t = l.target) == null ? void 0 : t.value, B = q(y); n("valueChange", B, e.node.path); }, N = k(() => { var y; let l = (y = e.node) == null ? void 0 : y.content; return l === null ? l = "null" : l === void 0 && (l = "undefined"), o.value === "string" ? `"${l}"` : l + ""; }), g = () => { const l = e.renderNodeValue; return l ? l({ node: e.node, defaultValue: N.value }) : N.value; }, T = () => { n("bracketsClick", !e.collapsed, e.node); }, P = () => { n("iconClick", !e.collapsed, e.node); }, x = () => { n("nodeClick", e.node), w.value && e.selectOnClickNode && n("selectedChange", e.node); }, D = (l) => { if (e.editable && !f.editing) { f.editing = !0; const y = (B) => { var t; B.target !== l.target && ((t = B.target) == null ? void 0 : t.parentElement) !== l.target && (f.editing = !1, document.removeEventListener("click", y)); }; document.removeEventListener("click", y), document.addEventListener("click", y); } }; return () => { const { node: l, pointer: y } = e; return d("div", { class: { "vjs-tree-node": !0, "has-selector": e.showSelectController, "has-carets": e.showIcon, "is-highlight": e.highlightSelectedNode && e.checked, dark: e.theme === "dark" }, onClick: x, style: e.style, "data-pointer": JSON.stringify(y) }, [e.showLineNumber && d("span", { class: "vjs-node-index" }, [l.id + 1]), d("div", { class: "vjs-indent" }, [Array.from(Array(l.level)).map((B, t) => d("div", { key: t, class: { "vjs-indent-unit": !0, "has-line": e.showLine } }, null)), e.showIcon && d(J, { nodeType: l.type, onClick: P }, null)]), l.key && d("span", { class: "vjs-key" }, [v(), d("span", { class: "vjs-colon" }, [`:${e.showKeyValueSpace ? " " : ""}`])]), d("span", null, [l.type !== "content" && l.content ? d(z, { data: l.content.toString(), onClick: T }, null) : d("span", { class: u.value, onClick: e.editable && (!e.editableTrigger || e.editableTrigger === "click") ? D : void 0, onDblclick: e.editable && e.editableTrigger === "dblclick" ? D : void 0 }, [e.editable && f.editing ? d("input", { value: N.value, onChange: b, style: { padding: "3px 8px", border: "1px solid #eee", boxShadow: "none", boxSizing: "border-box", borderRadius: 5, fontFamily: "inherit" } }, null) : g()]), l.showComma && d("span", null, [","]), e.showLength && e.collapsed && d("span", { class: "vjs-comment" }, [$(" // "), l.length, $(" items ")])])]); }; } }), W = /* @__PURE__ */ F({ name: "Tree", props: { ...K, // JSONLike data. data: { type: [String, Number, Boolean, Array, Object], default: null }, collapsedNodeLength: { type: Number, default: 1 / 0 }, // Define the depth of the tree, nodes greater than this depth will not be expanded. deep: { type: Number, default: 8 }, pathCollapsible: { type: Function, default: () => !1 }, // Data root path. rootPath: { type: String, default: "" }, // Whether to use virtual scroll, usually applied to big data. virtual: { type: Boolean, default: !1 }, // When using virtual scroll, set the height of tree. height: { type: Number, default: 400 }, // When using virtual scroll, define the height of each row. itemHeight: { type: Number, default: 20 }, // When there is a selection function, define the selected path. // For multiple selections, it is an array ['root.a','root.b'], for single selection, it is a string of 'root.a'. selectedValue: { type: [String, Array], default: () => "" }, // Collapsed control. collapsedOnClickBrackets: { type: Boolean, default: !0 }, style: Object, onSelectedChange: { type: Function }, theme: { type: String, default: "light" }, //json字符串位置映射 pointers: { type: Object, default: () => { } }, renderEffect: { type: Function } }, // slots: ['renderNodeKey', 'renderNodeValue'], emits: ["nodeClick", "bracketsClick", "iconClick", "selectedChange", "update:selectedValue", "update:data"], setup(e, { emit: n, slots: o, expose: u }) { const C = R(), v = k(() => V(e.data, e.rootPath)), m = (t, i) => v.value.reduce((c, a) => { var S; const r = a.level >= t || a.length >= i, h = (S = e.pathCollapsible) == null ? void 0 : S.call(e, a); return (a.type === "objectStart" || a.type === "arrayStart") && (r || h) ? { ...c, [a.path]: 1 } : c; }, {}), s = H({ translateY: 0, visibleData: null, hiddenPaths: m(e.deep, e.collapsedNodeLength) }), w = k(() => { let t = null; const i = [], c = v.value.length; for (let a = 0; a < c; a++) { const h = { ...v.value[a], id: a }, S = s.hiddenPaths[h.path]; if (t && t.path === h.path) { const L = t.type === "objectStart", j = { ...h, ...t, showComma: h.showComma, content: L ? "{...}" : "[...]", type: L ? "objectCollapsed" : "arrayCollapsed" }; t = null, i.push(j); } else if (S && !t) { t = h; continue; } else { if (t) continue; i.push(h); } } return i; }), f = k(() => { const t = e.selectedValue; return t && e.selectableType === "multiple" && Array.isArray(t) ? t : [t]; }), b = () => { var i; const t = w.value; if (e.virtual) { const c = e.height / e.itemHeight, a = ((i = C.value) == null ? void 0 : i.scrollTop) || 0, r = Math.floor(a / e.itemHeight); let h = r < 0 ? 0 : r + c > t.length ? t.length - c : r; h < 0 && (h = 0); const S = h + c; s.translateY = h * e.itemHeight, s.visibleData = t.filter((L, j) => j >= h && j < S); } else s.visibleData = t; }, N = () => { b(); }, g = ({ path: t }) => { const i = e.selectableType; if (i === "multiple") { const c = f.value.findIndex((r) => r === t), a = [...f.value]; c !== -1 ? a.splice(c, 1) : a.push(t), n("update:selectedValue", a), n("selectedChange", a, [...f.value]); } else if (i === "single" && f.value[0] !== t) { const [c] = f.value, a = t; n("update:selectedValue", a), n("selectedChange", a, c); } }, T = (t) => { n("nodeClick", t); }, P = (t, i) => { if (t) s.hiddenPaths = { ...s.hiddenPaths, [i]: 1 }; else { const c = { ...s.hiddenPaths }; delete c[i], s.hiddenPaths = c; } }, x = (t, i) => { e.collapsedOnClickBrackets && P(t, i.path), n("bracketsClick", t, i); }, D = (t, i) => { P(t, i.path), n("iconClick", t, i); }, l = (t, i) => { const c = O(e.data), a = e.rootPath; new Function("data", "val", `data${i.slice(a.length)}=val`)(c, t), n("update:data", c); }; Y(() => { w.value && b(); }), I(() => e.deep, (t) => { t && (s.hiddenPaths = m(t, e.collapsedNodeLength)); }), I(() => e.collapsedNodeLength, (t) => { t && (s.hiddenPaths = m(e.deep, t)); }); const y = {}; return u({ getContentPoint: () => y }), () => { const t = e.renderNodeKey ?? o.renderNodeKey, i = e.renderNodeValue ?? o.renderNodeValue, { pointers: c } = e, a = s.visibleData && s.visibleData.map((r, h) => { function S(M) { return M.replace(/\\u([0-9A-Fa-f]{4})/g, function(Z, Q) { return String.fromCodePoint(parseInt(Q, 16)); }); } let L = S(r.path); const j = c ? c[L] : ""; return e.renderEffect && e.renderEffect(j, r, h), j && (y[JSON.stringify(j)] = r.content), d(U, { key: r.id, node: r, collapsed: !!s.hiddenPaths[r.path], theme: e.theme, showDoubleQuotes: e.showDoubleQuotes, showLength: e.showLength, checked: f.value.includes(r.path), selectableType: e.selectableType, showLine: e.showLine, showLineNumber: e.showLineNumber, showSelectController: e.showSelectController, selectOnClickNode: e.selectOnClickNode, nodeSelectable: e.nodeSelectable, highlightSelectedNode: e.highlightSelectedNode, editable: e.editable, editableTrigger: e.editableTrigger, showIcon: e.showIcon, showKeyValueSpace: e.showKeyValueSpace, renderNodeKey: t, renderNodeValue: i, onNodeClick: T, onBracketsClick: x, onIconClick: D, onSelectedChange: g, onValueChange: l, style: e.itemHeight && e.itemHeight !== 20 ? { lineHeight: `${e.itemHeight}px` } : {}, pointer: c[L] }, null); }); return d("div", { ref: C, class: { "vjs-tree": !0, "is-virtual": e.virtual, dark: e.theme === "dark" }, onScroll: e.virtual ? N : void 0, style: e.showLineNumber ? { paddingLeft: `${Number(v.value.length.toString().length) * 12}px`, ...e.style } : e.style }, [e.virtual ? d("div", { class: "vjs-tree-list", style: { height: `${e.height}px` } }, [d("div", { class: "vjs-tree-list-holder", style: { height: `${w.value.length * e.itemHeight}px` } }, [d("div", { class: "vjs-tree-list-holder-inner", style: { transform: `translateY(${s.translateY}px)` } }, [a])])]) : a]); }; } }); function G(e) { e.component("jsonTree", W); } export { W as JsonTree, G as default };