json-diff-viewer-component 0.6.2 → 0.6.4
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.md +108 -43
- package/package.json +1 -1
- package/src/lib/styles.js +13 -8
- package/src/lib/viewer.js +23 -21
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/metaory/json-diff-viewer-component/master/public/logo.
|
|
2
|
+
<img src="https://raw.githubusercontent.com/metaory/json-diff-viewer-component/master/public/logo.png" alt="logo" height="128" />
|
|
3
3
|
<h2>json-diff-viewer</h2>
|
|
4
4
|
<h5>
|
|
5
5
|
Compare JSON side-by-side, visually
|
|
@@ -36,24 +36,50 @@ npm i json-diff-viewer-component
|
|
|
36
36
|
|
|
37
37
|
## Usage
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Every integration needs three things:
|
|
40
|
+
|
|
41
|
+
1. Import the package once to register the custom element.
|
|
42
|
+
2. Add `<json-diff-viewer>` to the page.
|
|
43
|
+
3. Provide both values using one of the data APIs below.
|
|
44
|
+
|
|
45
|
+
Import the package:
|
|
40
46
|
|
|
41
47
|
```js
|
|
42
48
|
import "json-diff-viewer-component";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<json-diff-viewer></json-diff-viewer>
|
|
53
|
+
```
|
|
43
54
|
|
|
55
|
+
Choose one data API. Root values must be non-null.
|
|
56
|
+
|
|
57
|
+
### `setData(left, right)` (Recommended)
|
|
58
|
+
|
|
59
|
+
Use for dynamic data. Both values update in one render:
|
|
60
|
+
|
|
61
|
+
```js
|
|
44
62
|
const viewer = document.querySelector("json-diff-viewer");
|
|
45
|
-
viewer.setData(
|
|
63
|
+
viewer.setData(
|
|
64
|
+
{ name: "foo", enabled: true },
|
|
65
|
+
{ name: "bar", enabled: true },
|
|
66
|
+
);
|
|
46
67
|
```
|
|
47
68
|
|
|
48
|
-
|
|
69
|
+
### Properties (Alternative)
|
|
70
|
+
|
|
71
|
+
Use when assigning JavaScript values independently. Rendering starts once both sides are set:
|
|
49
72
|
|
|
50
73
|
```js
|
|
51
|
-
|
|
74
|
+
viewer.left = { name: "foo" };
|
|
75
|
+
viewer.right = { name: "bar" };
|
|
76
|
+
|
|
77
|
+
console.log(viewer.left, viewer.right);
|
|
52
78
|
```
|
|
53
79
|
|
|
54
|
-
### HTML Attributes
|
|
80
|
+
### HTML Attributes (Alternative)
|
|
55
81
|
|
|
56
|
-
|
|
82
|
+
Use only for small, static, inline JSON. Each value is parsed with `JSON.parse()`, so invalid JSON throws:
|
|
57
83
|
|
|
58
84
|
```html
|
|
59
85
|
<json-diff-viewer
|
|
@@ -62,33 +88,32 @@ Static JSON can be passed as attributes. Values must be valid JSON strings:
|
|
|
62
88
|
></json-diff-viewer>
|
|
63
89
|
```
|
|
64
90
|
|
|
65
|
-
|
|
66
|
-
- Prefer `setData()` or property setters for dynamic or object data
|
|
91
|
+
Prefer `setData()` or properties for objects already available in JavaScript.
|
|
67
92
|
|
|
68
|
-
###
|
|
93
|
+
### Class Export
|
|
69
94
|
|
|
70
|
-
|
|
95
|
+
The registered class is exported only when a class reference is needed for testing or extension:
|
|
71
96
|
|
|
72
97
|
```js
|
|
73
|
-
|
|
74
|
-
viewer.right = { name: "bar" };
|
|
75
|
-
|
|
76
|
-
viewer.left; // read current left value
|
|
77
|
-
viewer.right; // read current right value
|
|
98
|
+
import { JsonDiffViewer } from "json-diff-viewer-component";
|
|
78
99
|
```
|
|
79
100
|
|
|
80
101
|
### Built-in Controls
|
|
81
102
|
|
|
82
|
-
The component toolbar includes:
|
|
103
|
+
No setup is required. The component toolbar includes:
|
|
83
104
|
|
|
84
105
|
- **Show only changed**: filter toggle (default: **on**); hides unchanged nodes
|
|
85
106
|
- **Collapse all** / **Expand all**: bulk expand/collapse
|
|
86
107
|
- **Node toggles**: click any object/array line to expand/collapse (synced across both panels)
|
|
87
108
|
|
|
109
|
+
### Framework Usage
|
|
110
|
+
|
|
111
|
+
The component remains the same custom element and uses `setData()` for reactive values.
|
|
112
|
+
|
|
88
113
|
<details>
|
|
89
|
-
<summary>
|
|
114
|
+
<summary>React and Vue examples</summary>
|
|
90
115
|
|
|
91
|
-
|
|
116
|
+
#### React
|
|
92
117
|
|
|
93
118
|
```jsx
|
|
94
119
|
import { useEffect, useRef } from "react";
|
|
@@ -107,7 +132,7 @@ function DiffViewer({ left, right }) {
|
|
|
107
132
|
}
|
|
108
133
|
```
|
|
109
134
|
|
|
110
|
-
|
|
135
|
+
#### Vue
|
|
111
136
|
|
|
112
137
|
```vue
|
|
113
138
|
<template>
|
|
@@ -115,25 +140,15 @@ function DiffViewer({ left, right }) {
|
|
|
115
140
|
</template>
|
|
116
141
|
|
|
117
142
|
<script setup>
|
|
118
|
-
import { ref,
|
|
143
|
+
import { ref, watchEffect } from "vue";
|
|
119
144
|
import "json-diff-viewer-component";
|
|
120
145
|
|
|
121
|
-
const props = defineProps(
|
|
122
|
-
left: Object,
|
|
123
|
-
right: Object,
|
|
124
|
-
});
|
|
125
|
-
|
|
146
|
+
const props = defineProps(["left", "right"]);
|
|
126
147
|
const viewerRef = ref(null);
|
|
127
148
|
|
|
128
|
-
|
|
129
|
-
(
|
|
130
|
-
|
|
131
|
-
if (viewerRef.value) {
|
|
132
|
-
viewerRef.value.setData(props.left, props.right);
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
{ immediate: true },
|
|
136
|
-
);
|
|
149
|
+
watchEffect(() => {
|
|
150
|
+
viewerRef.value?.setData(props.left, props.right);
|
|
151
|
+
});
|
|
137
152
|
</script>
|
|
138
153
|
```
|
|
139
154
|
|
|
@@ -141,18 +156,20 @@ watch(
|
|
|
141
156
|
|
|
142
157
|
## Diff Types
|
|
143
158
|
|
|
144
|
-
| Type
|
|
145
|
-
|
|
|
146
|
-
| Added
|
|
147
|
-
| Removed
|
|
148
|
-
| Modified | Yellow |
|
|
159
|
+
| Type | Default | Description |
|
|
160
|
+
| --- | --- | --- |
|
|
161
|
+
| Added | Green | Key exists only on the right |
|
|
162
|
+
| Removed | Red | Key exists only on the left |
|
|
163
|
+
| Modified | Yellow | Values differ, or a container has changed descendants |
|
|
149
164
|
|
|
150
165
|
## Styling
|
|
151
166
|
|
|
152
|
-
Override CSS custom properties
|
|
167
|
+
The component is fully usable without style configuration. Override CSS custom properties on `json-diff-viewer`. They inherit through the host into its shadow DOM. State foreground tokens also drive their derived backgrounds unless those backgrounds are overridden separately.
|
|
153
168
|
|
|
154
169
|
### Design Tokens
|
|
155
170
|
|
|
171
|
+
Tokens inherit through the host. Override only the values your theme needs.
|
|
172
|
+
|
|
156
173
|
```css
|
|
157
174
|
json-diff-viewer {
|
|
158
175
|
/* Diff colors */
|
|
@@ -171,8 +188,18 @@ json-diff-viewer {
|
|
|
171
188
|
--txt: #fafafa; /* Primary text */
|
|
172
189
|
--dim: #a1a1aa; /* Dimmed/secondary text */
|
|
173
190
|
|
|
174
|
-
/*
|
|
175
|
-
--
|
|
191
|
+
/* State backgrounds */
|
|
192
|
+
--add-bg: color-mix(in srgb, var(--add) 15%, transparent);
|
|
193
|
+
--rem-bg: color-mix(in srgb, var(--rem) 15%, transparent);
|
|
194
|
+
--mod-bg: color-mix(in srgb, var(--mod) 15%, transparent);
|
|
195
|
+
|
|
196
|
+
/* Controls and interaction */
|
|
197
|
+
--hover: rgb(0 0 0 / 3%); /* Diff row hover */
|
|
198
|
+
--control-bg: var(--bg2); /* Action button background */
|
|
199
|
+
--control-hover: rgb(0 0 0 / 5%); /* Action button hover */
|
|
200
|
+
--control-bdr: var(--bdr); /* Action button border */
|
|
201
|
+
--slider: var(--bdr); /* Slider active track */
|
|
202
|
+
--slider-thumb: var(--br); /* Slider thumb */
|
|
176
203
|
|
|
177
204
|
/* Syntax highlighting */
|
|
178
205
|
--key: #38bdf8; /* Object keys */
|
|
@@ -206,6 +233,43 @@ json-diff-viewer {
|
|
|
206
233
|
}
|
|
207
234
|
```
|
|
208
235
|
|
|
236
|
+
### Shadow Parts
|
|
237
|
+
|
|
238
|
+
Use `::part()` for structural overrides that are not shared design tokens:
|
|
239
|
+
|
|
240
|
+
| Parts | Elements |
|
|
241
|
+
| --- | --- |
|
|
242
|
+
| `toolbar`, `legend`, `legend-item`, `legend-added`, `legend-removed`, `legend-modified` | Summary and state legend |
|
|
243
|
+
| `actions`, `filter`, `filter-track`, `action-button`, `collapse-button`, `expand-button` | Viewer controls |
|
|
244
|
+
| `content`, `panel`, `panel-left`, `panel-right` | Diff layout and panels |
|
|
245
|
+
| `node`, `node-added`, `node-removed`, `node-modified`, `line` | Diff nodes and rows |
|
|
246
|
+
| `toggle`, `marker`, `marker-added`, `marker-removed`, `marker-modified` | Node controls and state markers |
|
|
247
|
+
| `key`, `separator`, `value`, `value-string`, `value-number`, `value-boolean`, `value-null` | JSON content |
|
|
248
|
+
| `bracket`, `preview`, `empty` | Supporting content and empty state |
|
|
249
|
+
|
|
250
|
+
Elements can expose multiple parts, so generic and specific selectors compose:
|
|
251
|
+
|
|
252
|
+
```css
|
|
253
|
+
json-diff-viewer {
|
|
254
|
+
--add: lime;
|
|
255
|
+
--add-bg: color-mix(in srgb, lime 20%, transparent);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
json-diff-viewer::part(toolbar) {
|
|
259
|
+
border-block-end-width: 4px;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
json-diff-viewer::part(node-added) {
|
|
263
|
+
border-inline-start: 3px solid var(--add);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
json-diff-viewer::part(collapse-button) {
|
|
267
|
+
border-radius: 999px;
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Parts are the supported structural API. Internal classes and other shadow elements remain private. `::part()` cannot select descendants, so target each listed part directly. Changing layout, padding, or typography on `node` and `line` can desynchronize corresponding rows between panels.
|
|
272
|
+
|
|
209
273
|
### Sizing
|
|
210
274
|
|
|
211
275
|
Set a height to get scrolling. Without one, the viewer grows to fit all content. Default border-radius is `12px`.
|
|
@@ -237,6 +301,7 @@ json-diff-viewer {
|
|
|
237
301
|
```bash
|
|
238
302
|
npm run dev # start dev server
|
|
239
303
|
npm run build # build for production
|
|
304
|
+
npm run preview # preview the production build
|
|
240
305
|
```
|
|
241
306
|
|
|
242
307
|
## License
|
package/package.json
CHANGED
package/src/lib/styles.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
export default `
|
|
2
2
|
:host {
|
|
3
3
|
--add: #22c55e; --rem: #ef4444; --mod: #eab308;
|
|
4
|
+
--add-bg: color-mix(in srgb, var(--add) 15%, transparent);
|
|
5
|
+
--rem-bg: color-mix(in srgb, var(--rem) 15%, transparent);
|
|
6
|
+
--mod-bg: color-mix(in srgb, var(--mod) 15%, transparent);
|
|
4
7
|
--bg: #18181b; --bg2: #27272a; --bdr: #3f3f46;
|
|
5
8
|
--txt: #fafafa; --dim: #a1a1aa;
|
|
6
9
|
--key: #38bdf8; --str: #a78bfa; --num: #34d399; --bool: #fb923c; --nul: #f472b6; --br: #71717a;
|
|
7
|
-
--
|
|
10
|
+
--hover: rgb(0 0 0 / 3%);
|
|
11
|
+
--control-bg: var(--bg2); --control-hover: rgb(0 0 0 / 5%); --control-bdr: var(--bdr);
|
|
12
|
+
--slider: var(--bdr); --slider-thumb: var(--br);
|
|
8
13
|
display: flex; flex-direction: column; font: 13px 'JetBrains Mono', 'Fira Code', monospace;
|
|
9
14
|
background: var(--bg); color: var(--txt); border-radius: 12px; overflow: hidden;
|
|
10
15
|
}
|
|
@@ -15,7 +20,7 @@ export default `
|
|
|
15
20
|
.node { padding-left: 1.25rem; }
|
|
16
21
|
.node.root { padding-left: 0; }
|
|
17
22
|
.line { display: flex; align-items: flex-start; gap: 0.5rem; padding: 2px 4px; border-radius: 4px; cursor: pointer; transition: background .15s; }
|
|
18
|
-
.line:hover { background:
|
|
23
|
+
.line:hover { background: var(--hover); }
|
|
19
24
|
.line.placeholder { cursor: default; pointer-events: none; }
|
|
20
25
|
.line.placeholder:hover { background: transparent; }
|
|
21
26
|
.tog { width: 1rem; flex-shrink: 0; color: var(--br); user-select: none; }
|
|
@@ -28,9 +33,9 @@ export default `
|
|
|
28
33
|
.val-boolean { color: var(--bool); }
|
|
29
34
|
.val-null { color: var(--nul); font-style: italic; }
|
|
30
35
|
.br { color: var(--br); }
|
|
31
|
-
.node.diff-added { background:
|
|
32
|
-
.node.diff-removed { background:
|
|
33
|
-
.node.diff-modified { background:
|
|
36
|
+
.node.diff-added { background: var(--add-bg); }
|
|
37
|
+
.node.diff-removed { background: var(--rem-bg); }
|
|
38
|
+
.node.diff-modified { background: var(--mod-bg); }
|
|
34
39
|
.node.diff-added .key { color: var(--add); }
|
|
35
40
|
.node.diff-removed .key { color: var(--rem); }
|
|
36
41
|
.node.diff-modified .key { color: var(--mod); }
|
|
@@ -47,13 +52,13 @@ export default `
|
|
|
47
52
|
.switch { display: inline-block; cursor: pointer; }
|
|
48
53
|
.checkbox { display: none; }
|
|
49
54
|
.slider { width: 48px; height: 24px; background-color: var(--bdr); border-radius: 16px; overflow: hidden; display: flex; align-items: center; border: 3px solid transparent; transition: .3s; box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.25) inset; cursor: pointer; }
|
|
50
|
-
.slider::before { content: ''; display: block; width: 100%; height: 100%; background-color: var(--
|
|
55
|
+
.slider::before { content: ''; display: block; width: 100%; height: 100%; background-color: var(--slider-thumb); transform: translateX(-24px); border-radius: 16px; transition: .3s; box-shadow: 0 0 10px 3px rgba(0, 0, 0, 0.25); }
|
|
51
56
|
.checkbox:checked ~ .slider::before { transform: translateX(24px); box-shadow: 0 0 10px 3px rgba(0, 0, 0, 0.25); }
|
|
52
57
|
.checkbox:checked ~ .slider { background-color: var(--slider); }
|
|
53
58
|
.checkbox:active ~ .slider::before { transform: translate(0); }
|
|
54
|
-
.btn-collapse, .btn-expand { padding: 0.5rem; background: var(--
|
|
59
|
+
.btn-collapse, .btn-expand { padding: 0.5rem; background: var(--control-bg); border: 1px solid var(--control-bdr); border-radius: 10px; color: var(--txt); cursor: pointer; transition: background .15s, border-color .15s; display: flex; align-items: center; justify-content: center; }
|
|
55
60
|
.btn-collapse svg, .btn-expand svg { width: 18px; height: 18px; }
|
|
56
|
-
.btn-collapse:hover, .btn-expand:hover { background:
|
|
61
|
+
.btn-collapse:hover, .btn-expand:hover { background: var(--control-hover); box-shadow: 0 0 4px var(--control-bdr); }
|
|
57
62
|
.stat { display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: .35rem; }
|
|
58
63
|
.stat .dot { width: 8px; height: 8px; }
|
|
59
64
|
.stat-added .dot { background: var(--add); }
|
package/src/lib/viewer.js
CHANGED
|
@@ -2,6 +2,8 @@ import { diff, TYPE } from "./diff.js";
|
|
|
2
2
|
import styles from "./styles.js";
|
|
3
3
|
|
|
4
4
|
const STAT_TYPES = ["added", "removed", "modified"];
|
|
5
|
+
const ENTITIES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
6
|
+
const escapeHtml = (value) => String(value).replace(/[&<>"']/g, (char) => ENTITIES[char]);
|
|
5
7
|
|
|
6
8
|
const format = (val) => {
|
|
7
9
|
if (val === null) return ["null", "null"];
|
|
@@ -20,7 +22,7 @@ const filterChildren = (children, showOnlyChanged) =>
|
|
|
20
22
|
const isExpanded = (proxy, path) => proxy[path] !== false;
|
|
21
23
|
|
|
22
24
|
const buildKeyHtml = (key, root, hidden = false) =>
|
|
23
|
-
root ? "" : `<span class="key"${hidden ? ' style="visibility: hidden;"' : ""}>${key}</span><span class="colon"${hidden ? ' style="visibility: hidden;"' : ""}>:</span>`;
|
|
25
|
+
root ? "" : `<span class="key" part="key"${hidden ? ' style="visibility: hidden;"' : ""}>${escapeHtml(key)}</span><span class="colon" part="separator"${hidden ? ' style="visibility: hidden;"' : ""}>:</span>`;
|
|
24
26
|
|
|
25
27
|
const buildRootClass = (root) => root ? " root" : "";
|
|
26
28
|
|
|
@@ -136,30 +138,30 @@ class JsonDiffViewer extends HTMLElement {
|
|
|
136
138
|
|
|
137
139
|
#render() {
|
|
138
140
|
if (!this.#tree) {
|
|
139
|
-
this.shadowRoot.innerHTML = `<style>${styles}</style><div class="empty">Provide left and right JSON</div>`;
|
|
141
|
+
this.shadowRoot.innerHTML = `<style>${styles}</style><div class="empty" part="empty">Provide left and right JSON</div>`;
|
|
140
142
|
return;
|
|
141
143
|
}
|
|
142
144
|
const panel = this.shadowRoot.querySelector('.panel');
|
|
143
145
|
const scroll = { top: panel?.scrollTop || 0, left: panel?.scrollLeft || 0 };
|
|
144
146
|
this.shadowRoot.innerHTML = `
|
|
145
147
|
<style>${styles}</style>
|
|
146
|
-
<div class="stats">
|
|
147
|
-
<div class="stats-items">
|
|
148
|
-
${STAT_TYPES.map((t) => `<div class="stat stat-${t}"><span class="dot"></span>${this.#stats[t]} ${t.replace("_", " ")}</div>`).join("")}
|
|
148
|
+
<div class="stats" part="toolbar">
|
|
149
|
+
<div class="stats-items" part="legend">
|
|
150
|
+
${STAT_TYPES.map((t) => `<div class="stat stat-${t}" part="legend-item legend-${t}"><span class="dot" part="marker marker-${t}"></span>${this.#stats[t]} ${t.replace("_", " ")}</div>`).join("")}
|
|
149
151
|
</div>
|
|
150
|
-
<div class="stats-buttons">
|
|
151
|
-
<label class="switch" aria-label="Show only changed" title="Show only changed">
|
|
152
|
+
<div class="stats-buttons" part="actions">
|
|
153
|
+
<label class="switch" part="filter" aria-label="Show only changed" title="Show only changed">
|
|
152
154
|
<input type="checkbox" class="checkbox" data-action="filter" ${this.#showOnlyChanged ? "checked" : ""}>
|
|
153
|
-
<div class="slider"></div>
|
|
155
|
+
<div class="slider" part="filter-track"></div>
|
|
154
156
|
</label>
|
|
155
|
-
<button class="btn-collapse" data-action="collapse" title="Collapse all"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M9 15H6q-.425 0-.712-.288T5 14t.288-.712T6 13h4q.425 0 .713.288T11 14v4q0 .425-.288.713T10 19t-.712-.288T9 18zm6-6h3q.425 0 .713.288T19 10t-.288.713T18 11h-4q-.425 0-.712-.288T13 10V6q0-.425.288-.712T14 5t.713.288T15 6z"/></svg></button>
|
|
156
|
-
<button class="btn-expand" data-action="expand" title="Expand all"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M7 17h3q.425 0 .713.288T11 18t-.288.713T10 19H6q-.425 0-.712-.288T5 18v-4q0-.425.288-.712T6 13t.713.288T7 14zM17 7h-3q-.425 0-.712-.288T13 6t.288-.712T14 5h4q.425 0 .713.288T19 6v4q0 .425-.288.713T18 11t-.712-.288T17 10z"/></svg></button>
|
|
157
|
+
<button class="btn-collapse" part="action-button collapse-button" data-action="collapse" title="Collapse all"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M9 15H6q-.425 0-.712-.288T5 14t.288-.712T6 13h4q.425 0 .713.288T11 14v4q0 .425-.288.713T10 19t-.712-.288T9 18zm6-6h3q.425 0 .713.288T19 10t-.288.713T18 11h-4q-.425 0-.712-.288T13 10V6q0-.425.288-.712T14 5t.713.288T15 6z"/></svg></button>
|
|
158
|
+
<button class="btn-expand" part="action-button expand-button" data-action="expand" title="Expand all"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M7 17h3q.425 0 .713.288T11 18t-.288.713T10 19H6q-.425 0-.712-.288T5 18v-4q0-.425.288-.712T6 13t.713.288T7 14zM17 7h-3q-.425 0-.712-.288T13 6t.288-.712T14 5h4q.425 0 .713.288T19 6v4q0 .425-.288.713T18 11t-.712-.288T17 10z"/></svg></button>
|
|
157
159
|
</div>
|
|
158
160
|
</div>
|
|
159
|
-
<div class="container">
|
|
161
|
+
<div class="container" part="content">
|
|
160
162
|
${["left", "right"]
|
|
161
163
|
.map((side) => {
|
|
162
|
-
return `<div class="panel" data-side="${side}">${this.#renderNode(this.#tree, side, "")}</div>`;
|
|
164
|
+
return `<div class="panel" part="panel panel-${side}" data-side="${side}">${this.#renderNode(this.#tree, side, "")}</div>`;
|
|
163
165
|
})
|
|
164
166
|
.join("")}
|
|
165
167
|
</div>`;
|
|
@@ -182,18 +184,18 @@ class JsonDiffViewer extends HTMLElement {
|
|
|
182
184
|
const otherValue = side === 'left' ? node.right : node.left;
|
|
183
185
|
const [val, type] = format(otherValue);
|
|
184
186
|
const hiddenKey = buildKeyHtml(node.key, root, true);
|
|
185
|
-
return `<div class="node${rootClass}"><div class="line placeholder">${hiddenKey}<span class="val-${type}" style="visibility: hidden;">${val}</span></div></div>`;
|
|
187
|
+
return `<div class="node${rootClass}" part="node"><div class="line placeholder" part="line">${hiddenKey}<span class="val-${type}" part="value value-${type}" style="visibility: hidden;">${escapeHtml(val)}</span></div></div>`;
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
if (value !== undefined && !node.isArray && !node.isObject) {
|
|
189
191
|
const [val, type] = format(value);
|
|
190
192
|
if (placeholder) {
|
|
191
|
-
return `<div class="node${rootClass}"><div class="line placeholder">${keyHtml}<span class="val-${type}"${hidden}>${val}</span></div></div>`;
|
|
193
|
+
return `<div class="node${rootClass}" part="node"><div class="line placeholder" part="line">${keyHtml}<span class="val-${type}" part="value value-${type}"${hidden}>${escapeHtml(val)}</span></div></div>`;
|
|
192
194
|
}
|
|
193
195
|
const hasDiff = node.hasDiff && node.type !== TYPE.UNCHANGED;
|
|
194
196
|
const diffClass = hasDiff ? `diff-${node.type}` : "";
|
|
195
197
|
const nodeDiffClass = hasDiff ? ` ${diffClass}` : "";
|
|
196
|
-
return `<div class="node${rootClass}${nodeDiffClass}"><div class="line"><span class="tog"></span>${keyHtml}<span class="val-${type}">${val}</span></div></div>`;
|
|
198
|
+
return `<div class="node${rootClass}${nodeDiffClass}" part="node${hasDiff ? ` node-${node.type}` : ""}"><div class="line" part="line"><span class="tog" part="toggle"></span>${keyHtml}<span class="val-${type}" part="value value-${type}">${escapeHtml(val)}</span></div></div>`;
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
const [open, close] = getBrackets(node.isArray);
|
|
@@ -207,25 +209,25 @@ class JsonDiffViewer extends HTMLElement {
|
|
|
207
209
|
|
|
208
210
|
if (placeholder) {
|
|
209
211
|
if (!expanded) {
|
|
210
|
-
return `<div class="node${rootClass}"><div class="line placeholder">${keyHtml}<span class="br"${hidden}>${open}</span><span class="preview"${hidden}>${preview}</span><span class="br"${hidden}>${close}</span></div></div>`;
|
|
212
|
+
return `<div class="node${rootClass}" part="node"><div class="line placeholder" part="line">${keyHtml}<span class="br" part="bracket"${hidden}>${open}</span><span class="preview" part="preview"${hidden}>${preview}</span><span class="br" part="bracket"${hidden}>${close}</span></div></div>`;
|
|
211
213
|
}
|
|
212
|
-
return `<div class="node${rootClass}"><div class="line placeholder">${keyHtml}<span class="br"${hidden}>${open}</span></div>${childrenHtml}<div class="line placeholder"><span class="br"${hidden}>${close}</span></div></div>`;
|
|
214
|
+
return `<div class="node${rootClass}" part="node"><div class="line placeholder" part="line">${keyHtml}<span class="br" part="bracket"${hidden}>${open}</span></div>${childrenHtml}<div class="line placeholder" part="line"><span class="br" part="bracket"${hidden}>${close}</span></div></div>`;
|
|
213
215
|
}
|
|
214
216
|
|
|
215
217
|
const hasDiff = node.hasDiff && node.type !== TYPE.UNCHANGED;
|
|
216
218
|
const diffClass = hasDiff ? `diff-${node.type}` : "";
|
|
217
219
|
const hasChildDiff = node.hasDiff && children.some((c) => c.hasDiff);
|
|
218
220
|
const dotType = node.type === TYPE.UNCHANGED ? "modified" : node.type;
|
|
219
|
-
const dot = hasChildDiff ? `<span class="dot dot-${dotType}"></span>` : "";
|
|
221
|
+
const dot = hasChildDiff ? `<span class="dot dot-${dotType}" part="marker marker-${dotType}"></span>` : "";
|
|
220
222
|
const nodeDiffClass = hasDiff && !hasChildDiff ? ` ${diffClass}` : "";
|
|
221
223
|
const toggle = expanded ? "▼" : "▶";
|
|
222
|
-
const dataPath = ` data-p="${currentPath}"`;
|
|
224
|
+
const dataPath = ` data-p="${escapeHtml(currentPath)}"`;
|
|
223
225
|
|
|
224
226
|
if (!expanded) {
|
|
225
|
-
return `<div class="node${rootClass}${nodeDiffClass}"><div class="line"${dataPath}><span class="tog">${toggle}</span>${dot}${keyHtml}<span class="br">${open}</span><span class="preview">${preview}</span><span class="br">${close}</span></div></div>`;
|
|
227
|
+
return `<div class="node${rootClass}${nodeDiffClass}" part="node${hasDiff ? ` node-${node.type}` : ""}"><div class="line" part="line"${dataPath}><span class="tog" part="toggle">${toggle}</span>${dot}${keyHtml}<span class="br" part="bracket">${open}</span><span class="preview" part="preview">${preview}</span><span class="br" part="bracket">${close}</span></div></div>`;
|
|
226
228
|
}
|
|
227
229
|
|
|
228
|
-
return `<div class="node${rootClass}${nodeDiffClass}"><div class="line"${dataPath}><span class="tog">${toggle}</span>${dot}${keyHtml}<span class="br">${open}</span></div>${childrenHtml}<div class="line"><span class="tog"></span><span class="br">${close}</span></div></div>`;
|
|
230
|
+
return `<div class="node${rootClass}${nodeDiffClass}" part="node${hasDiff ? ` node-${node.type}` : ""}"><div class="line" part="line"${dataPath}><span class="tog" part="toggle">${toggle}</span>${dot}${keyHtml}<span class="br" part="bracket">${open}</span></div>${childrenHtml}<div class="line" part="line"><span class="tog" part="toggle"></span><span class="br" part="bracket">${close}</span></div></div>`;
|
|
229
231
|
}
|
|
230
232
|
|
|
231
233
|
#bind() {
|