json-viewer-element 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # <json-viewer> Element
2
2
 
3
+ [简体中文](./README.md) | English
4
+
3
5
  > 🌈 A lightweight, modern Web Component for JSON visualization and interaction.
4
6
 
5
7
  ## Features
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # <json-viewer> Element
2
2
 
3
+ 简体中文 | [English](./README.en.md)
4
+
3
5
  > 🌈 一个轻量、现代的 JSON 可视化与交互 Web 组件
4
6
 
5
7
  ## 功能特性
@@ -1,424 +1 @@
1
- const togglerSvg = "data:image/svg+xml;charset=utf-8;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMCAwIDggOC04IDh6IiBmaWxsPSIjNjY2Ii8+PC9zdmc+";
2
- const tpl = document.createElement("template");
3
- tpl.innerHTML = `
4
- <style>
5
- /* Light Theme (default) */
6
- :host {
7
- --jv-bg-color: #ffffff;
8
- --jv-border-color: #ddd;
9
- --jv-text-color: #111;
10
- --jv-key-color: #111;
11
- --jv-string-color: #42b983;
12
- --jv-number-color: #fc1e70;
13
- --jv-boolean-color: #fc1e70;
14
- --jv-null-color: #e08331;
15
- --jv-undefined-color: #b0b0b0;
16
- --jv-function-color: #067bca;
17
- --jv-regexp-color: #fc1e70;
18
- --jv-copy-bg: #eee;
19
- --jv-copy-text: #333;
20
- --jv-ellipsis-color: #999999;
21
- --jv-ellipsis-bg: #eeeeee;
22
- --jv-hover-shadow: rgba(0,0,0,0.1);
23
- }
24
-
25
- /* Dark Theme */
26
- :host([theme="dark"]) {
27
- --jv-bg-color: #23272f;
28
- --jv-border-color: #2c313a;
29
- --jv-text-color: #d4d4d4;
30
- --jv-key-color: #79c0ff;
31
- --jv-string-color: #a5d6a7;
32
- --jv-number-color: #e2b86b;
33
- --jv-boolean-color: #ff7b72;
34
- --jv-null-color: #ffab70;
35
- --jv-undefined-color: #d2a8ff;
36
- --jv-function-color: #c678dd;
37
- --jv-regexp-color: #56b6c2;
38
- --jv-copy-bg: #3a3f4b;
39
- --jv-copy-text: #fff;
40
- --jv-ellipsis-color: #6e7681;
41
- --jv-ellipsis-bg: #2c313a;
42
- --jv-hover-shadow: rgba(0,0,0,0.4);
43
- }
44
-
45
- :host {
46
- display: block;
47
- width: 100%;
48
- max-width: 100%;
49
- font-family: Consolas, Menlo, Courier, monospace;
50
- font-size: 14px;
51
- padding: 8px;
52
- overflow-x: auto;
53
- box-sizing: border-box;
54
- position: relative;
55
- background-color: var(--jv-bg-color);
56
- color: var(--jv-text-color);
57
- }
58
-
59
- :host([boxed]) {
60
- border: 1px solid var(--jv-border-color);
61
- border-radius: 4px;
62
- padding: 16px;
63
- transition: box-shadow 0.2s ease;
64
- }
65
- :host([boxed]:hover) {
66
- box-shadow: 0 2px 8px var(--jv-hover-shadow);
67
- }
68
- #root:has(+.align-left) {
69
- margin-top: 16px;
70
- }
71
- .jv-copy {
72
- cursor: pointer;
73
- font-size: 12px;
74
- background: var(--jv-copy-bg);
75
- color: var(--jv-copy-text);
76
- padding: 4px 8px;
77
- border-radius: 3px;
78
- opacity: 0;
79
- transition: opacity 0.2s ease;
80
- }
81
- :host(:hover) .jv-copy {
82
- opacity: 1;
83
- }
84
- slot[name="copy-button"] {
85
- position: absolute;
86
- top: 8px;
87
- right: 8px;
88
- z-index: 10;
89
- opacity: 0;
90
- transition: opacity 0.2s ease;
91
- display: block !important;
92
- }
93
- slot[name="copy-button"][hidden] {
94
- display: none !important;
95
- }
96
- slot[name="copy-button"].align-left {
97
- left: 8px;
98
- right: auto;
99
- }
100
- slot[name="copy-button"].align-right {
101
- right: 8px;
102
- left: auto;
103
- }
104
- :host(:hover) slot[name="copy-button"] {
105
- opacity: 1;
106
- }
107
- .jv-toggle {
108
- background-image: url("${togglerSvg}");
109
- background-repeat: no-repeat;
110
- background-size: contain;
111
- background-position: center center;
112
- cursor: pointer;
113
- width: 10px;
114
- height: 10px;
115
- margin-right: 2px;
116
- display: inline-block;
117
- transition: rotate .1s;
118
- }
119
- .jv-toggle.open {
120
- rotate: 90deg;
121
- }
122
- .jv-key {
123
- color: var(--jv-key-color);
124
- }
125
- .jv-string {
126
- color: var(--jv-string-color);
127
- }
128
- .jv-number {
129
- color: var(--jv-number-color);
130
- }
131
- .jv-boolean {
132
- color: var(--jv-boolean-color);
133
- }
134
- .jv-null {
135
- color: var(--jv-null-color);
136
- }
137
- .jv-undefined {
138
- color: var(--jv-undefined-color);
139
- }
140
- .jv-function {
141
- color: var(--jv-function-color);
142
- }
143
- .jv-regexp {
144
- color: var(--jv-regexp-color);
145
- }
146
- .jv-list {
147
- margin-left: 16px;
148
- }
149
- .jv-item:not(:has(.jv-toggle)) .jv-key {
150
- margin-left: 12px;
151
- }
152
- .jv-item:not(:last-child):after {
153
- content: ',';
154
- }
155
- .jv-node > .jv-ellipsis {
156
- display: none;
157
- }
158
- .jv-node.empty > .jv-list {
159
- display: inline-block;
160
- margin-inline: 4px;
161
- }
162
- .jv-node.collapsed > .jv-list,
163
- .jv-node.collapsed.empty > .jv-ellipsis {
164
- display: none;
165
- }
166
- .jv-node.collapsed > .jv-ellipsis {
167
- color: var(--jv-ellipsis-color);
168
- background-color: var(--jv-ellipsis-bg);
169
- display: inline-block;
170
- line-height: 0.9;
171
- font-size: 0.85em;
172
- vertical-align: 2px;
173
- cursor: pointer;
174
- user-select: none;
175
- padding: 2px 4px;
176
- margin: 0px 4px;
177
- border-radius: 3px;
178
- }
179
- </style>
180
- <div id="root" part="root"></div>
181
- <slot name="copy-button" part="copy-button" hidden>
182
- <span class="jv-copy"></span>
183
- </slot>
184
- `;
185
- class JsonViewerElement extends HTMLElement {
186
- constructor() {
187
- super();
188
- this._value = null;
189
- this.root = this.attachShadow({ mode: "open" });
190
- this.root.appendChild(tpl.content.cloneNode(true));
191
- this.container = this.root.getElementById("root");
192
- }
193
- static get observedAttributes() {
194
- return [
195
- "value",
196
- "expand-depth",
197
- "copyable",
198
- "sort",
199
- "boxed",
200
- "theme",
201
- "parse"
202
- ];
203
- }
204
- connectedCallback() {
205
- this.render();
206
- }
207
- attributeChangedCallback() {
208
- this.render();
209
- }
210
- set value(v) {
211
- if (v === this._value)
212
- return;
213
- this._value = v;
214
- this.render();
215
- }
216
- get value() {
217
- return this._value ?? this.getAttribute("value");
218
- }
219
- set sort(v) {
220
- this.setAttribute("sort", v ? "true" : "false");
221
- }
222
- get sort() {
223
- return this.hasAttribute("sort");
224
- }
225
- set parse(v) {
226
- this.setAttribute("parse", v ? "true" : "false");
227
- }
228
- get parse() {
229
- return this.getAttribute("parse") !== "false";
230
- }
231
- set copyable(v) {
232
- if (v === false) {
233
- this.removeAttribute("copyable");
234
- } else {
235
- this.setAttribute("copyable", JSON.stringify(v));
236
- }
237
- }
238
- get copyable() {
239
- if (!this.hasAttribute("copyable"))
240
- return false;
241
- const attr = this.getAttribute("copyable");
242
- const defaultCopyableOptions = { copyText: "Copy", copiedText: "Copied", timeout: 2e3, align: "right" };
243
- if (attr === "" || attr === null)
244
- return defaultCopyableOptions;
245
- try {
246
- return {
247
- ...defaultCopyableOptions,
248
- ...JSON.parse(attr)
249
- };
250
- } catch {
251
- return defaultCopyableOptions;
252
- }
253
- }
254
- get expandDepth() {
255
- return Number(this.getAttribute("expand-depth") ?? 1);
256
- }
257
- /**
258
- * Copy text to clipboard. Uses Clipboard API if available, otherwise fallback.
259
- */
260
- copyText(text) {
261
- if (navigator.clipboard) {
262
- this.copyText = (text2) => navigator.clipboard.writeText(text2);
263
- return this.copyText(text);
264
- }
265
- this.copyText = (text2) => new Promise((resolve, reject) => {
266
- const input = document.createElement("input");
267
- input.value = text2;
268
- document.body.appendChild(input);
269
- input.select();
270
- if (document.execCommand("copy")) {
271
- document.body.removeChild(input);
272
- resolve();
273
- } else {
274
- document.body.removeChild(input);
275
- reject(new Error("Copy failed"));
276
- }
277
- });
278
- return this.copyText(text);
279
- }
280
- /**
281
- * Render the JSON viewer.
282
- */
283
- render() {
284
- if (typeof this.value === "string" && this.parse) {
285
- try {
286
- this._value = JSON.parse(this.value);
287
- } catch {
288
- }
289
- }
290
- this.container.innerHTML = "";
291
- this.container.appendChild(this.build(this._value, 0));
292
- const copyableOptions = this.copyable;
293
- if (copyableOptions) {
294
- const align = copyableOptions.align || "right";
295
- const copySlot = this.root.querySelector('slot[name="copy-button"]');
296
- const customCopyButton = copySlot.assignedElements()[0];
297
- const defaultCopyBtn = this.root.querySelector(".jv-copy");
298
- copySlot.hidden = false;
299
- copySlot.className = `align-${align}`;
300
- if (!customCopyButton) {
301
- let copyTimeout;
302
- defaultCopyBtn.textContent = copyableOptions.copyText;
303
- const newBtn = defaultCopyBtn.cloneNode(true);
304
- defaultCopyBtn.replaceWith(newBtn);
305
- newBtn.textContent = copyableOptions.copyText;
306
- newBtn.addEventListener("click", () => {
307
- const textToCopy = JSON.stringify(this._value, null, 2);
308
- this.copyText(textToCopy).then(() => {
309
- newBtn.textContent = copyableOptions.copiedText;
310
- copyTimeout = window.setTimeout(() => {
311
- newBtn.textContent = copyableOptions.copyText;
312
- clearTimeout(copyTimeout);
313
- }, copyableOptions.timeout);
314
- this.dispatchEvent(new CustomEvent("copy-success", {
315
- detail: { text: textToCopy, options: copyableOptions }
316
- }));
317
- }).catch(() => {
318
- console.warn("Failed to copy text to clipboard");
319
- this.dispatchEvent(new CustomEvent("copy-error", {
320
- detail: { text: textToCopy, options: copyableOptions }
321
- }));
322
- });
323
- });
324
- }
325
- }
326
- }
327
- /**
328
- * Recursively build the JSON tree.
329
- */
330
- build(data, depth) {
331
- if (data === null)
332
- return this.leaf("null", "jv-null");
333
- if (data === void 0)
334
- return this.leaf("undefined", "jv-undefined");
335
- if (typeof data === "boolean")
336
- return this.leaf(String(data), "jv-boolean");
337
- if (typeof data === "number")
338
- return this.leaf(String(data), "jv-number");
339
- if (typeof data === "string")
340
- return this.leaf(`"${data}"`, "jv-string");
341
- if (typeof data === "function")
342
- return this.leaf("<function>", "jv-function");
343
- if (data instanceof RegExp)
344
- return this.leaf("<regexp>", "jv-regexp");
345
- if (data instanceof Date)
346
- return this.leaf(`"${data.toLocaleString()}"`, "jv-string");
347
- const isArr = Array.isArray(data);
348
- const node = document.createElement("span");
349
- node.className = "jv-node";
350
- node.setAttribute("part", "node");
351
- const list = document.createElement("div");
352
- list.className = "jv-list";
353
- list.setAttribute("part", "list");
354
- const keys = isArr ? this.sort ? [...data.keys()].sort((a, b) => a - b) : [...data.keys()] : this.sort ? Object.keys(data).sort() : Object.keys(data);
355
- for (const k of keys) {
356
- const item = document.createElement("div");
357
- const childNode = this.build(data[k], depth + 1);
358
- item.className = "jv-item";
359
- if (childNode instanceof Element && childNode.classList.contains("jv-node")) {
360
- const childToggle = childNode.querySelector(".jv-toggle");
361
- if (childToggle) {
362
- childToggle.remove();
363
- item.append(childToggle);
364
- }
365
- }
366
- if (!isArr) {
367
- const keySpan = document.createElement("span");
368
- keySpan.className = "jv-key";
369
- keySpan.setAttribute("part", "key");
370
- keySpan.textContent = `"${k}": `;
371
- item.append(keySpan);
372
- }
373
- item.append(childNode);
374
- list.append(item);
375
- }
376
- const toggle = document.createElement("span");
377
- toggle.className = "jv-toggle";
378
- toggle.setAttribute("part", "toggle");
379
- if (!node.classList.contains("collapsed")) {
380
- toggle.classList.add("open");
381
- }
382
- toggle.addEventListener("click", () => {
383
- node.classList.toggle("collapsed");
384
- toggle.classList.toggle("open");
385
- this.dispatchEvent(new CustomEvent("toggle", {
386
- detail: {
387
- node,
388
- data,
389
- isCollapsed: node.classList.contains("collapsed")
390
- }
391
- }));
392
- });
393
- const ellipsis = document.createElement("span");
394
- ellipsis.className = "jv-ellipsis";
395
- ellipsis.setAttribute("part", "ellipsis");
396
- ellipsis.textContent = `...${keys.length}`;
397
- ellipsis.addEventListener("click", () => {
398
- node.classList.remove("collapsed");
399
- toggle.classList.add("open");
400
- });
401
- if (depth >= this.expandDepth) {
402
- node.classList.add("collapsed");
403
- toggle.classList.remove("open");
404
- }
405
- if (!keys.length)
406
- node.classList.add("empty");
407
- node.append(toggle, isArr ? "[" : "{", ellipsis, list, isArr ? "]" : "}");
408
- return node;
409
- }
410
- /**
411
- * Create a leaf node for primitive values.
412
- */
413
- leaf(text, cls) {
414
- const s = document.createElement("span");
415
- s.className = `jv-value ${cls}`;
416
- s.setAttribute("part", `value ${cls.replace("jv-", "")}`);
417
- s.textContent = text;
418
- return s;
419
- }
420
- }
421
- customElements.define("json-viewer", JsonViewerElement);
422
- export {
423
- JsonViewerElement
424
- };
1
+ const e=document.createElement("template");e.innerHTML='\n<style>\n/* Light Theme (default) */\n:host {\n --jv-bg-color: #ffffff;\n --jv-border-color: #ddd;\n --jv-text-color: #111;\n --jv-key-color: #111;\n --jv-string-color: #42b983;\n --jv-number-color: #fc1e70;\n --jv-boolean-color: #fc1e70;\n --jv-null-color: #e08331;\n --jv-undefined-color: #b0b0b0;\n --jv-function-color: #067bca;\n --jv-regexp-color: #fc1e70;\n --jv-copy-bg: #eee;\n --jv-copy-text: #333;\n --jv-ellipsis-color: #999999;\n --jv-ellipsis-bg: #eeeeee;\n --jv-hover-shadow: rgba(0,0,0,0.1);\n}\n\n/* Dark Theme */\n:host([theme="dark"]) {\n --jv-bg-color: #23272f;\n --jv-border-color: #2c313a;\n --jv-text-color: #d4d4d4;\n --jv-key-color: #79c0ff;\n --jv-string-color: #a5d6a7;\n --jv-number-color: #e2b86b;\n --jv-boolean-color: #ff7b72;\n --jv-null-color: #ffab70;\n --jv-undefined-color: #d2a8ff;\n --jv-function-color: #c678dd;\n --jv-regexp-color: #56b6c2;\n --jv-copy-bg: #3a3f4b;\n --jv-copy-text: #fff;\n --jv-ellipsis-color: #6e7681;\n --jv-ellipsis-bg: #2c313a;\n --jv-hover-shadow: rgba(0,0,0,0.4);\n}\n\n:host {\n display: block;\n width: 100%;\n max-width: 100%;\n font-family: Consolas, Menlo, Courier, monospace;\n font-size: 14px;\n padding: 8px;\n overflow-x: auto;\n box-sizing: border-box;\n position: relative;\n background-color: var(--jv-bg-color);\n color: var(--jv-text-color);\n}\n\n:host([boxed]) {\n border: 1px solid var(--jv-border-color);\n border-radius: 4px;\n padding: 16px;\n transition: box-shadow 0.2s ease;\n}\n:host([boxed]:hover) {\n box-shadow: 0 2px 8px var(--jv-hover-shadow);\n}\n#root:has(+.align-left) {\n margin-top: 16px;\n}\n.jv-copy {\n cursor: pointer;\n font-size: 12px;\n background: var(--jv-copy-bg);\n color: var(--jv-copy-text);\n padding: 4px 8px;\n border-radius: 3px;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n:host(:hover) .jv-copy {\n opacity: 1;\n}\nslot[name="copy-button"] {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 10;\n opacity: 0;\n transition: opacity 0.2s ease;\n display: block !important;\n}\nslot[name="copy-button"][hidden] {\n display: none !important;\n}\nslot[name="copy-button"].align-left {\n left: 8px;\n right: auto;\n}\nslot[name="copy-button"].align-right {\n right: 8px;\n left: auto;\n}\n:host(:hover) slot[name="copy-button"] {\n opacity: 1;\n}\n.jv-toggle {\n background-image: url("data:image/svg+xml;charset=utf-8;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMCAwIDggOC04IDh6IiBmaWxsPSIjNjY2Ii8+PC9zdmc+");\n background-repeat: no-repeat;\n background-size: contain;\n background-position: center center;\n cursor: pointer;\n width: 10px;\n height: 10px;\n margin-right: 2px;\n display: inline-block;\n transition: rotate .1s;\n}\n.jv-toggle.open {\n rotate: 90deg;\n}\n.jv-key {\n color: var(--jv-key-color);\n}\n.jv-string {\n color: var(--jv-string-color);\n}\n.jv-number {\n color: var(--jv-number-color);\n}\n.jv-boolean {\n color: var(--jv-boolean-color);\n}\n.jv-null {\n color: var(--jv-null-color);\n}\n.jv-undefined {\n color: var(--jv-undefined-color);\n}\n.jv-function {\n color: var(--jv-function-color);\n}\n.jv-regexp {\n color: var(--jv-regexp-color);\n}\n.jv-list {\n margin-left: 16px;\n}\n.jv-item:not(:has(.jv-toggle)) .jv-key {\n margin-left: 12px;\n}\n.jv-item:not(:last-child):after {\n content: \',\';\n}\n.jv-node > .jv-ellipsis {\n display: none;\n}\n.jv-node.empty > .jv-list {\n display: inline-block;\n margin-inline: 4px;\n}\n.jv-node.collapsed > .jv-list,\n.jv-node.collapsed.empty > .jv-ellipsis {\n display: none;\n}\n.jv-node.collapsed > .jv-ellipsis {\n color: var(--jv-ellipsis-color);\n background-color: var(--jv-ellipsis-bg);\n display: inline-block;\n line-height: 0.9;\n font-size: 0.85em;\n vertical-align: 2px;\n cursor: pointer;\n user-select: none;\n padding: 2px 4px;\n margin: 0px 4px;\n border-radius: 3px;\n}\n</style>\n<div id="root" part="root"></div>\n<slot name="copy-button" part="copy-button" hidden>\n <span class="jv-copy"></span>\n</slot>\n';class t extends HTMLElement{constructor(){super(),this._value=null,this.root=this.attachShadow({mode:"open"}),this.root.appendChild(e.content.cloneNode(!0)),this.container=this.root.getElementById("root")}static get observedAttributes(){return["value","expand-depth","copyable","sort","boxed","theme","parse"]}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}set value(e){e!==this._value&&(this._value=e,this.render())}get value(){return this._value??this.getAttribute("value")}set sort(e){this.setAttribute("sort",e?"true":"false")}get sort(){return this.hasAttribute("sort")}set parse(e){this.setAttribute("parse",e?"true":"false")}get parse(){return"false"!==this.getAttribute("parse")}set copyable(e){!1===e?this.removeAttribute("copyable"):this.setAttribute("copyable",JSON.stringify(e))}get copyable(){if(!this.hasAttribute("copyable"))return!1;const e=this.getAttribute("copyable"),t={copyText:"Copy",copiedText:"Copied",timeout:2e3,align:"right"};if(""===e||null===e)return t;try{return{...t,...JSON.parse(e)}}catch{return t}}get expandDepth(){return Number(this.getAttribute("expand-depth")??1)}copyText(e){return navigator.clipboard?(this.copyText=e=>navigator.clipboard.writeText(e),this.copyText(e)):(this.copyText=e=>new Promise((t,n)=>{const o=document.createElement("input");o.value=e,document.body.appendChild(o),o.select(),document.execCommand("copy")?(document.body.removeChild(o),t()):(document.body.removeChild(o),n(new Error("Copy failed")))}),this.copyText(e))}render(){if("string"==typeof this.value&&this.parse)try{this._value=JSON.parse(this.value)}catch{}this.container.innerHTML="",this.container.appendChild(this.build(this._value,0));const e=this.copyable;if(e){const t=e.align||"right",n=this.root.querySelector('slot[name="copy-button"]'),o=n.assignedElements()[0],r=this.root.querySelector(".jv-copy");if(n.hidden=!1,n.className=`align-${t}`,!o){let t;r.textContent=e.copyText;const n=r.cloneNode(!0);r.replaceWith(n),n.textContent=e.copyText,n.addEventListener("click",()=>{const o=JSON.stringify(this._value,null,2);this.copyText(o).then(()=>{n.textContent=e.copiedText,t=window.setTimeout(()=>{n.textContent=e.copyText,clearTimeout(t)},e.timeout),this.dispatchEvent(new CustomEvent("copy-success",{detail:{text:o,options:e}}))}).catch(()=>{console.warn("Failed to copy text to clipboard"),this.dispatchEvent(new CustomEvent("copy-error",{detail:{text:o,options:e}}))})})}}}build(e,t){if(null===e)return this.leaf("null","jv-null");if(void 0===e)return this.leaf("undefined","jv-undefined");if("boolean"==typeof e)return this.leaf(String(e),"jv-boolean");if("number"==typeof e)return this.leaf(String(e),"jv-number");if("string"==typeof e)return this.leaf(`"${e}"`,"jv-string");if("function"==typeof e)return this.leaf("<function>","jv-function");if(e instanceof RegExp)return this.leaf("<regexp>","jv-regexp");if(e instanceof Date)return this.leaf(`"${e.toLocaleString()}"`,"jv-string");const n=Array.isArray(e),o=document.createElement("span");o.className="jv-node",o.setAttribute("part","node");const r=document.createElement("div");r.className="jv-list",r.setAttribute("part","list");const s=n?this.sort?[...e.keys()].sort((e,t)=>e-t):[...e.keys()]:this.sort?Object.keys(e).sort():Object.keys(e);for(const l of s){const o=document.createElement("div"),s=this.build(e[l],t+1);if(o.className="jv-item",s instanceof Element&&s.classList.contains("jv-node")){const e=s.querySelector(".jv-toggle");e&&(e.remove(),o.append(e))}if(!n){const e=document.createElement("span");e.className="jv-key",e.setAttribute("part","key"),e.textContent=`"${l}": `,o.append(e)}o.append(s),r.append(o)}const i=document.createElement("span");i.className="jv-toggle",i.setAttribute("part","toggle"),o.classList.contains("collapsed")||i.classList.add("open"),i.addEventListener("click",()=>{o.classList.toggle("collapsed"),i.classList.toggle("open"),this.dispatchEvent(new CustomEvent("toggle",{detail:{node:o,data:e,isCollapsed:o.classList.contains("collapsed")}}))});const a=document.createElement("span");return a.className="jv-ellipsis",a.setAttribute("part","ellipsis"),a.textContent=`...${s.length}`,a.addEventListener("click",()=>{o.classList.remove("collapsed"),i.classList.add("open")}),t>=this.expandDepth&&(o.classList.add("collapsed"),i.classList.remove("open")),s.length||o.classList.add("empty"),o.append(i,n?"[":"{",a,r,n?"]":"}"),o}leaf(e,t){const n=document.createElement("span");return n.className=`jv-value ${t}`,n.setAttribute("part",`value ${t.replace("jv-","")}`),n.textContent=e,n}}customElements.define("json-viewer",t);export{t as JsonViewerElement};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "json-viewer-element",
3
3
  "type": "module",
4
- "version": "1.0.1",
4
+ "version": "1.0.2",
5
5
  "packageManager": "pnpm@10.18.2",
6
6
  "description": "A custom element for viewing and interacting with JSON data.",
7
7
  "author": "Lruihao (https://lruihao.cn)",