UNPKG

@anilkumarthakur/vue3-json-viewer

Version:

A beautiful, customizable JSON viewer component for Vue 3 with TypeScript support, dark/light mode, expand/collapse controls, and syntax highlighting.

570 lines (569 loc) 13.7 kB
import { createTextVNode as e, createVNode as t, defineComponent as n, inject as r, isVNode as i, provide as a, reactive as o, ref as s, watch as c } from "vue"; //#region src/utils/type-guards.ts var l = (e) => e === null, u = (e) => e === void 0, d = (e) => typeof e == "string", f = (e) => typeof e == "number", p = (e) => typeof e == "boolean", m = (e) => e instanceof Date, h = (e) => e instanceof RegExp, g = (e) => Array.isArray(e), _ = (e) => typeof e == "object" && !!e && !Array.isArray(e) && !(e instanceof Date) && !(e instanceof RegExp), v = { null: "#f38ba8", string: "#a6e3a1", number: "#fab387", boolean: "#f9e2af", date: "#94e2d5", regexp: "#cba6f7", arrayEmpty: "#9399b2", array: "#89b4fa", objectEmpty: "#9399b2", object: "#f5c2e7", default: "#cdd6f4" }, y = { null: "#e03131", string: "#2f9e44", number: "#e8590c", boolean: "#f59f00", date: "#0c8599", regexp: "#7048e8", arrayEmpty: "#868e96", array: "#1971c2", objectEmpty: "#868e96", object: "#c2255c", default: "#343a40" }, b = [ "#f38ba8", "#fab387", "#f9e2af", "#a6e3a1", "#89dceb", "#cba6f7" ], x = [ "#e03131", "#e8590c", "#f59f00", "#2f9e44", "#1098ad", "#7048e8" ], S = (e, t) => { let n = t ? v : y; return l(e) || u(e) ? n.null : d(e) ? n.string : f(e) ? n.number : p(e) ? n.boolean : m(e) ? n.date : h(e) ? n.regexp : g(e) ? e.length === 0 ? n.arrayEmpty : n.array : _(e) ? Object.keys(e).length === 0 ? n.objectEmpty : n.object : n.default; }, C = (e, t) => { let n = t ? b : x; return n[e % n.length] ?? n[0] ?? "#cdd6f4"; }, w = /^[A-Za-z_$][A-Za-z0-9_$]*$/; function T(e, t, n) { return n ? `${e}[${t}]` : w.test(t) ? `${e}.${t}` : `${e}[${JSON.stringify(t)}]`; } var E = Symbol("json-viewer-context"); function D(e) { let { defaultExpanded: t, onToggle: n, onCopy: r } = e, i = o(/* @__PURE__ */ new Map()), a = (e) => { let n = i.get(e); return n === void 0 ? t.value : n; }, s = (e, t, r) => { i.set(e, r), n?.({ path: e, key: t, expanded: r }); }, c = (e, t) => { s(e, t, !a(e)); }, l = (e) => { i.clear(), t.value = e; }; return { isExpanded: a, setExpanded: s, toggle: c, expandAll: () => l(!0), collapseAll: () => l(!1), reset: l, notifyCopy: (e, t, n) => { r?.({ path: e, key: t, value: n }); } }; } function O(e) { a(E, e); } function k() { return r(E, null); } //#endregion //#region src/components/JsonViewer/components/RootControls.tsx var A = () => t("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, [t("polyline", { points: "6 9 12 15 18 9" }, null)]), j = () => t("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, [t("polyline", { points: "18 15 12 9 6 15" }, null)]), M = /* @__PURE__ */ n({ name: "RootControls", props: { darkMode: { type: Boolean, required: !0 } }, emits: ["expand-all", "collapse-all"], setup(n, { emit: r }) { let i = () => r("expand-all"), a = () => r("collapse-all"), o = (e) => ["jv-control-btn", e ? "jv-control-btn-dark" : "jv-control-btn-light"]; return () => t("div", { class: "jv-root-controls" }, [t("button", { class: o(n.darkMode), onClick: i, title: "Expand All" }, [t(A, null, null), e("Expand All")]), t("button", { class: o(n.darkMode), onClick: a, title: "Collapse All" }, [t(j, null, null), e("Collapse All")])]); } }), N = (e) => l(e) ? "null" : u(e) ? "undefined" : d(e) ? `"${e}"` : p(e) ? e ? "true" : "false" : f(e) ? String(e) : m(e) ? `"${e.toISOString()}"` : h(e) ? e.toString() : String(e), P = (e, t) => { let n = t ? "-dark" : "-light"; return l(e) || u(e) ? `jv-null jv-null${n}` : d(e) ? `jv-string jv-string${n}` : f(e) ? `jv-number jv-number${n}` : p(e) ? `jv-boolean jv-boolean${n}` : m(e) ? `jv-date jv-date${n}` : h(e) ? `jv-regexp jv-regexp${n}` : t ? "jv-value-dark" : "jv-value-light"; }, F = (e) => e === "object" ? ["{", "}"] : ["[", "]"], I = (e, t, n) => e === "object" ? "obj" : t ? "empty" : String(n), L = (e, t) => { let n = e === "object" ? "key" : "item"; return `${t} ${t === 1 ? n : `${n}s`}`; }, R = (e) => { if (typeof document > "u") return !1; let t = document.createElement("textarea"); t.value = e, t.setAttribute("readonly", ""), t.style.position = "fixed", t.style.top = "-9999px", t.style.left = "-9999px", document.body.appendChild(t); let n = document.getSelection(), r = n && n.rangeCount > 0 ? n.getRangeAt(0) : null; t.select(); let i = !1; try { i = document.execCommand("copy") === !0; } catch {} return document.body.removeChild(t), n && r && (n.removeAllRanges(), n.addRange(r)), i; }, z = async (e) => { let t; try { let n = JSON.stringify(e, null, 2); t = n === void 0 ? String(e) : n; } catch { return !1; } if (typeof navigator < "u" && navigator.clipboard && typeof navigator.clipboard.writeText == "function") try { return await navigator.clipboard.writeText(t), !0; } catch {} return R(t); }, B = 1500, V = () => t("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, [t("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }, null), t("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" }, null)]), H = () => t("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, [t("polyline", { points: "20 6 9 17 4 12" }, null)]), U = /* @__PURE__ */ n({ name: "CopyButton", props: { darkMode: { type: Boolean, required: !0 }, data: { type: [ Object, Array, String, Number, Boolean, Function ], default: void 0 }, path: { type: String, default: "$" }, parentKey: { type: [String, Number], default: "" } }, setup(e) { let n = s(!1), r = k(), i = (t) => { t.stopPropagation(), z(e.data).then((t) => { t && (n.value = !0, r?.notifyCopy(e.path ?? "$", String(e.parentKey ?? ""), e.data), setTimeout(() => { n.value = !1; }, B)); }); }; return () => t("button", { class: ["jv-copy-btn", e.darkMode ? "jv-copy-btn-dark" : "jv-copy-btn-light"], onClick: i, title: n.value ? "Copied!" : "Copy" }, [n.value ? t(H, null, null) : t(V, null, null)]); } }), W = /* @__PURE__ */ n({ name: "TypeBadge", props: { type: { type: String, required: !0 } }, setup(e, { slots: n }) { return () => t("span", { class: ["jv-type-badge", e.type === "object" ? "jv-type-object" : "jv-type-array"] }, [n.default?.()]); } }); //#endregion //#region src/components/JsonViewer/components/ContainerRow.tsx function G(e) { return typeof e == "function" || Object.prototype.toString.call(e) === "[object Object]" && !i(e); } var K = /* @__PURE__ */ n({ name: "ContainerRow", props: { parentKey: { type: [String, Number], required: !0 }, isArrayItem: { type: Boolean, required: !0 }, isEmpty: { type: Boolean, required: !0 }, isExpanded: { type: Boolean, required: !0 }, size: { type: Number, required: !0 }, darkMode: { type: Boolean, required: !0 }, keyColor: { type: String, required: !0 }, bracketColor: { type: String, required: !0 }, data: { type: [ Object, Array, String, Number, Boolean ], required: !0 }, kind: { type: String, required: !0 }, path: { type: String, required: !0 } }, emits: ["toggle"], setup(n, { emit: r }) { let i = () => { r("toggle"); }; return () => { let [r, a] = F(n.kind), o = I(n.kind, n.isEmpty, n.size), s = L(n.kind, n.size), c = !n.isArrayItem && n.parentKey; return t("div", { class: "jv-row" }, [ c && t("span", { class: "jv-key jv-clickable", style: { color: n.keyColor }, onClick: i }, [ e("\""), n.parentKey, e("\"") ]), c && t("span", { class: "jv-colon jv-clickable", onClick: i }, [e(":")]), t("span", { class: "jv-toggle jv-type-indicator", style: { color: n.bracketColor }, onClick: i }, [t(W, { type: n.kind }, G(o) ? o : { default: () => [o] }), n.isEmpty ? t("span", null, [r, a]) : t("span", null, [n.isExpanded ? r : `${r}...${a}`])]), !n.isEmpty && !n.isExpanded && t("span", { class: ["jv-count", n.darkMode ? "jv-count-dark" : "jv-count-light"], onClick: i }, [s]), t(U, { darkMode: n.darkMode, data: n.data, path: n.path, parentKey: n.parentKey }, null) ]); }; } }), q = /* @__PURE__ */ n({ name: "PrimitiveValue", props: { data: { type: [ Object, Array, String, Number, Boolean, Function ], default: void 0 }, parentKey: { type: [String, Number], required: !0 }, isArrayItem: { type: Boolean, required: !0 }, isLast: { type: Boolean, required: !0 }, darkMode: { type: Boolean, required: !0 }, keyColor: { type: String, required: !0 }, path: { type: String, required: !0 } }, setup(n) { return () => { let r = !n.isArrayItem && n.parentKey; return t("div", { class: "jv-item jv-primitive" }, [t("div", { class: "jv-row" }, [ r && t("span", { class: "jv-key", style: { color: n.keyColor } }, [ e("\""), n.parentKey, e("\"") ]), r && t("span", { class: "jv-colon" }, [e(": ")]), t("span", { class: P(n.data, n.darkMode) }, [N(n.data)]), !n.isLast && t("span", { class: "jv-comma" }, [e(",")]), t(U, { darkMode: n.darkMode, data: n.data, path: n.path, parentKey: n.parentKey }, null) ])]); }; } }), J = "$", Y = /* @__PURE__ */ n({ name: "JsonNode", props: { data: { type: [ Object, Array, String, Number, Boolean, Function ], default: void 0 }, parentKey: { type: [String, Number], default: "" }, level: { type: Number, default: 0 }, darkMode: { type: Boolean, default: !0 }, expanded: { type: Boolean, default: !0 }, isArrayItem: { type: Boolean, default: !1 }, isLast: { type: Boolean, default: !0 }, path: { type: String, default: J } }, setup(e) { let n = k(); if (!n) { let t = s(e.expanded ?? !0); c(() => e.expanded, (e) => { n?.reset(e ?? !0); }), n = D({ defaultExpanded: t }), O(n); } let r = n, i = () => { r.toggle(e.path ?? J, String(e.parentKey ?? "")); }, a = (n, a, o) => { let s = e.level ?? 0, c = e.darkMode ?? !0, l = e.path ?? J, u = C(s, c), d = a.length === 0, f = n === "array", p = r.isExpanded(l); return t("div", { class: "jv-item" }, [ t(K, { parentKey: e.parentKey ?? "", isArrayItem: e.isArrayItem ?? !1, isEmpty: d, isExpanded: p, size: a.length, darkMode: c, keyColor: S(e.data, c), bracketColor: u, data: e.data, kind: n, path: l, onToggle: i }, null), p && !d && t("div", { class: "jv-children", style: { borderColor: u + "40" } }, [a.map(([n, r], i) => { let o = T(l, n, f); return t(Y, { key: n, data: r, parentKey: n, level: s + 1, darkMode: e.darkMode, isArrayItem: f, isLast: i === a.length - 1, path: o }, null); })]), p && !d && t("div", { class: "jv-row" }, [t("span", { class: "jv-bracket", style: { color: u } }, [o, e.isLast ? "" : ","])]) ]); }; return () => { let n = e.level ?? 0, i = e.darkMode ?? !0, o = n === 0, s = e.data, c = ["jv-node", { "jv-root": o, "jv-dark": i && o, "jv-light": !i && o }], l = o && (_(s) || g(s)); if (_(s)) { let e = Object.entries(s); return t("div", { class: c }, [l && t(M, { darkMode: i, "onExpand-all": r.expandAll, "onCollapse-all": r.collapseAll }, null), a("object", e, "}")]); } if (g(s)) { let e = s.map((e, t) => [String(t), e]); return t("div", { class: c }, [l && t(M, { darkMode: i, "onExpand-all": r.expandAll, "onCollapse-all": r.collapseAll }, null), a("array", e, "]")]); } return t("div", { class: c }, [t(q, { data: s, parentKey: e.parentKey ?? "", isArrayItem: e.isArrayItem ?? !1, isLast: e.isLast ?? !0, darkMode: i, keyColor: S(s, i), path: e.path ?? J }, null)]); }; } }), X = "$", Z = /* @__PURE__ */ n({ name: "JsonViewer", props: { data: { type: [ Object, Array, String, Number, Boolean ], default: () => ({}) }, level: { type: Number, default: 0 }, parentKey: { type: [String, Number], default: "" }, darkMode: { type: Boolean, default: !0 }, expanded: { type: Boolean, default: !0 } }, emits: { toggle: (e) => typeof e.path == "string" && typeof e.expanded == "boolean", copy: (e) => typeof e.path == "string" }, setup(e, { emit: n }) { let r = D({ defaultExpanded: s(e.expanded ?? !0), onToggle: (e) => n("toggle", e), onCopy: (e) => n("copy", e) }); return O(r), c(() => e.expanded, (e) => { r.reset(e ?? !0); }), () => t(Y, { darkMode: e.darkMode ?? !0, data: e.data ?? {}, level: e.level ?? 0, path: X }, null); } }), Q = { install(e) { e.component("JsonViewer", Z), e.component("JsonNode", Y); } }; //#endregion export { Y as JsonNode, Z as JsonViewer, Q as JsonViewerPlugin, Q as default }; //# sourceMappingURL=index.es.js.map