fix .json overwriting

This commit is contained in:
2026-04-23 15:40:07 +02:00
parent 31dd117d91
commit 44f8ecdc85
7 changed files with 130 additions and 172 deletions

View File

@@ -2,7 +2,6 @@
createJsonTree({
container: domElement,
data: jsonData,
expandInitially = true // optional: expand all nodes
onChange: (data) => { }, // optional: callback on change
onSave: (data) => { } // optional: if set, a save button will be added
});
@@ -12,7 +11,7 @@ function createJsonTree({
data,
onChange = () => {},
onSave = null,
expandInitially = true
readonly = false
}) {
container.innerHTML = "";
container.classList.add("json-tree-root");
@@ -22,8 +21,6 @@ function createJsonTree({
let lastSnapshot = clone(data);
const expandedPaths = new Set();
function clone(obj) {
return JSON.parse(JSON.stringify(obj));
}
@@ -70,6 +67,7 @@ function createJsonTree({
}
function undo() {
if (readonly) return;
if (!history.length) return;
redoStack.push(clone(data));
data = history.pop();
@@ -77,6 +75,7 @@ function createJsonTree({
}
function redo() {
if (readonly) return;
if (!redoStack.length) return;
history.push(clone(data));
data = redoStack.pop();
@@ -84,6 +83,7 @@ function createJsonTree({
}
function save() {
if (readonly) return;
if (onSave) onSave(data);
}
@@ -93,6 +93,7 @@ function createJsonTree({
}
function remove(key, parent) {
if (readonly) return;
if (key === null || parent == null) return;
if (!confirm("Wirklich löschen?")) return;
@@ -109,28 +110,10 @@ function createJsonTree({
render();
}
function collectPaths(obj, path = "root") {
if (typeof obj !== "object" || obj === null) return;
expandedPaths.add(path);
const entries = Array.isArray(obj)
? obj.map((v, i) => [i, v])
: Object.entries(obj);
for (const [k, v] of entries) {
collectPaths(v, `${path}.${k}`);
}
}
if (expandInitially) {
collectPaths(data);
}
function render() {
container.innerHTML = "";
if (onSave) {
if (onSave && !readonly) {
const controls = document.createElement("div");
controls.className = "json-controls";
@@ -174,10 +157,13 @@ function createJsonTree({
const removeBtn = document.createElement("span");
removeBtn.className = "json-remove";
removeBtn.textContent = " [x]";
removeBtn.onclick = (evt) => {
evt.stopPropagation();
remove(key, parent);
};
if (!readonly) {
removeBtn.onclick = (evt) => {
evt.stopPropagation();
remove(key, parent);
};
}
// OBJECT / ARRAY
if (typeof value === "object" && value !== null) {
@@ -201,81 +187,67 @@ function createJsonTree({
const children = document.createElement("div");
children.className = "json-children";
const toggleState = () => {
if (expandedPaths.has(path)) {
expandedPaths.delete(path);
children.classList.add("collapsed");
} else {
expandedPaths.add(path);
children.classList.remove("collapsed");
}
keyLabel.onclick = toggle.onclick = () => {
if (readonly) return;
children.classList.toggle("collapsed");
};
keyLabel.onclick = toggle.onclick = toggleState;
if (!readonly) {
addBtn.onclick = () => {
let newKey = "";
let newValue;
if (expandedPaths.has(path)) {
children.classList.remove("collapsed");
} else {
children.classList.add("collapsed");
feedbox({
title: `<span style="color:#f44336">Key</span> hinzufügen`,
message: `
<div style="display:flex;flex-direction:column;gap:8px;">
${!isArray ? `
<input id="newJsonKeyName" placeholder="Key Name" style="padding:6px;" />
` : ``}
<select id="newJsonValueType">
<option value="text">Text</option>
<option value="number">Zahl</option>
<option value="boolean">Boolean</option>
<option value="object">Objekt</option>
<option value="array">Array</option>
</select>
</div>
`,
buttons: {
yes: {
text: '<b>Anlegen</b>',
onClick: () => {
pushHistory();
if (!isArray) {
const input = document.querySelector('#newJsonKeyName');
newKey = input?.value?.trim();
if (!newKey) return;
}
const selectedType = document.querySelector('#newJsonValueType').value;
switch (selectedType) {
case "number": newValue = 0; break;
case "boolean": newValue = false; break;
case "object": newValue = {}; break;
case "array": newValue = []; break;
default: newValue = "";
}
if (isArray) value.push(newValue);
else value[newKey] = newValue;
onChange(data);
render();
}
},
no: { text: "Abbrechen" }
}
});
};
}
addBtn.onclick = () => {
let newKey = "";
let newValue;
feedbox({
title: `<span style="color:#f44336">Key</span> hinzufügen`,
message: `
<div style="display:flex;flex-direction:column;gap:8px;">
${!isArray ? `
<input id="newJsonKeyName" placeholder="Key Name" style="padding:6px;" />
` : ``}
<select id="newJsonValueType">
<option value="text">Text</option>
<option value="number">Zahl</option>
<option value="boolean">Boolean</option>
<option value="object">Objekt</option>
<option value="array">Array</option>
</select>
</div>
`,
buttons: {
yes: {
text: '<b>Anlegen</b>',
onClick: () => {
pushHistory();
if (!isArray) {
const input = document.querySelector('#newJsonKeyName');
newKey = input?.value?.trim();
if (!newKey) return;
}
const selectedType = document.querySelector('#newJsonValueType').value;
switch (selectedType) {
case "number": newValue = 0; break;
case "boolean": newValue = false; break;
case "object": newValue = {}; break;
case "array": newValue = []; break;
default: newValue = "";
}
if (isArray) {
value.push(newValue);
} else {
value[newKey] = newValue;
}
onChange(data);
render();
}
},
no: { text: 'Abbrechen' }
}
});
};
const inner = document.createElement("div");
const entries = isArray
@@ -292,9 +264,8 @@ function createJsonTree({
header.appendChild(keyLabel);
header.appendChild(toggle);
header.appendChild(addBtn);
if (key !== null) header.appendChild(removeBtn);
if (!readonly) header.appendChild(addBtn);
if (key !== null && !readonly) header.appendChild(removeBtn);
wrapper.appendChild(header);
wrapper.appendChild(children);
@@ -308,18 +279,22 @@ function createJsonTree({
input.className = "json-input string";
input.value = value;
input.oninput = () => {
pushHistory();
parent[key] = input.value;
autoResize(input);
onChange(data);
};
if (readonly) {
input.disabled = true;
} else {
input.oninput = () => {
pushHistory();
parent[key] = input.value;
autoResize(input);
onChange(data);
};
}
setTimeout(() => autoResize(input), 0);
wrapper.appendChild(keySpan);
wrapper.appendChild(input);
if (key !== null) wrapper.appendChild(removeBtn);
if (key !== null && !readonly) wrapper.appendChild(removeBtn);
return wrapper;
}
@@ -330,18 +305,22 @@ function createJsonTree({
input.className = "json-input number";
input.value = value;
input.oninput = () => {
pushHistory();
parent[key] = Number(input.value);
autoResize(input);
onChange(data);
};
if (readonly) {
input.disabled = true;
} else {
input.oninput = () => {
pushHistory();
parent[key] = Number(input.value);
autoResize(input);
onChange(data);
};
}
setTimeout(() => autoResize(input), 0);
wrapper.appendChild(keySpan);
wrapper.appendChild(input);
if (key !== null) wrapper.appendChild(removeBtn);
if (key !== null && !readonly) wrapper.appendChild(removeBtn);
return wrapper;
}
@@ -351,15 +330,19 @@ function createJsonTree({
input.type = "checkbox";
input.checked = value;
input.onchange = () => {
pushHistory();
parent[key] = input.checked;
onChange(data);
};
if (readonly) {
input.disabled = true;
} else {
input.onchange = () => {
pushHistory();
parent[key] = input.checked;
onChange(data);
};
}
wrapper.appendChild(keySpan);
wrapper.appendChild(input);
if (key !== null) wrapper.appendChild(removeBtn);
if (key !== null && !readonly) wrapper.appendChild(removeBtn);
return wrapper;
}
@@ -368,8 +351,7 @@ function createJsonTree({
wrapper.appendChild(keySpan);
wrapper.appendChild(span);
if (key !== null) wrapper.appendChild(removeBtn);
if (key !== null && !readonly) wrapper.appendChild(removeBtn);
return wrapper;
}