import '../component/EditJsonSheet.js';
class SectionsFormManager {
constructor() {
this.sections = [];
this.sectionsContainer = document.getElementById('sections');
}
display(sections, onChange) {
this.sections = sections.map((section) => {
return new SectionItemManager(
section,
this.createSection(section),
onChange)
})
this.sections.forEach((item => {
item.displayJSONCards()
}))
}
createSection(section) {
const sectionElement = document.createElement('div');
sectionElement.className = 'section';
sectionElement.dataset.filename = section.filename
sectionElement.dataset.name = section.name
const titleContainer = document.createElement('div');
titleContainer.className = 'section-title-container';
const title = document.createElement('h2');
title.className = "section-title";
title.textContent = section.name;
titleContainer.appendChild(title);
sectionElement.appendChild(titleContainer);
sectionElement.id = section.filename;
this.sectionsContainer.appendChild(sectionElement);
return sectionElement
}
data() {
const data = this.sections.map((section) => {
return section.section
})
return Array.from(data);
}
}
class SectionItemManager {
constructor(section, container, onChange) {
this.section = section
this.container = container
this.onChange = onChange
this.editJsonSheet = document.getElementById('editJsonSheet');
}
displayJSONCards() {
let cardContent = this.container.querySelector('.card-content')
if (cardContent !== null) this.container.innerHTML = ''
this.container.appendChild(this.createCard(this.section.content, 'root', null, this.section.filename));
}
createCard(obj, key, parent, cardTitle) {
const card = document.createElement('div');
card.className = 'card';
const header = document.createElement('div');
header.className = 'card-header';
header.textContent = key === 'root' ? cardTitle : key;
header.onclick = () => {
if (key !== 'root') {
this.editKey(parent, key)
return
}
this.editJsonSheet.show(
JSON.stringify(this.section.content, null, 2),
cardTitle,
(value) => {
this.section.content = value
this.displayJSONCards()
this.notifyChange()
})
};
const actions = document.createElement('div');
actions.className = 'card-actions';
const addBtn = document.createElement('button');
addBtn.className = 'add-property-btn';
addBtn.innerHTML = '';
addBtn.onclick = () => this.addProperty(obj);
actions.appendChild(addBtn);
if (key !== 'root') {
const deleteCardBtn = document.createElement('button');
deleteCardBtn.className = 'delete-btn';
deleteCardBtn.innerHTML = '';
deleteCardBtn.onclick = () => this.confirmDeleteProperty(parent, key);
actions.appendChild(deleteCardBtn);
}
header.appendChild(actions);
card.appendChild(header);
const content = document.createElement('div');
content.className = 'card-content';
const isArray = Array.isArray(obj)
for (const [k, v] of Object.entries(obj)) {
const item = document.createElement('div');
const cardValueContainer = document.createElement('div');
cardValueContainer.className = 'card-value-container';
item.appendChild(cardValueContainer);
const itemKey = document.createElement('span');
itemKey.className = 'card-key';
itemKey.onclick = () => {
if (isArray) return
this.editKey(obj, k)
};
cardValueContainer.appendChild(itemKey);
if (typeof v === 'object' && v !== null) {
item.appendChild(this.createCard(v, k, obj, null));
itemKey.textContent = isArray ? `${key}[${k}]` : ''
} else {
item.className = 'card-item';
itemKey.textContent = k.replace(/_/g, ' ')
if (typeof v === 'boolean') {
// Create a container for the entire boolean input
const booleanContainer = document.createElement('div');
booleanContainer.className = 'boolean-container';
const checkboxContainer = document.createElement('div');
checkboxContainer.className = 'card-value checkbox-container';
const itemValue = document.createElement('input');
itemValue.type = 'checkbox';
itemValue.className = 'card-value checkbox';
itemValue.checked = v;
const valueLabel = document.createElement('span');
valueLabel.className = 'checkbox-value';
valueLabel.textContent = v.toString();
const updateValue = () => {
const newValue = !itemValue.checked;
itemValue.checked = newValue;
valueLabel.textContent = newValue.toString();
this.updateValue(obj, k, newValue, typeof v);
};
// Add click handlers to both container and checkbox
booleanContainer.onclick = (e) => {
if (e.target !== itemValue) { // Prevent double-toggle when clicking checkbox
updateValue();
}
};
itemValue.onchange = () => {
valueLabel.textContent = itemValue.checked.toString();
this.updateValue(obj, k, itemValue.checked, typeof v);
};
checkboxContainer.appendChild(itemValue);
checkboxContainer.appendChild(valueLabel);
// Move the key inside the boolean container
booleanContainer.appendChild(itemKey);
booleanContainer.appendChild(checkboxContainer);
cardValueContainer.appendChild(booleanContainer);
} else if (this.isColorValue(v)) {
const itemValue = document.createElement('input');
itemValue.type = 'color';
itemValue.className = 'card-value';
itemValue.value = v;
itemValue.onchange = () => this.updateValue(obj, k, itemValue.value, typeof v);
cardValueContainer.appendChild(itemValue);
} else {
const itemValue = document.createElement('textarea');
itemValue.className = 'card-value';
itemValue.value = v;
itemValue.onchange = () => this.updateValue(obj, k, itemValue.value, typeof v);
cardValueContainer.appendChild(itemValue);
}
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
deleteBtn.innerHTML = '';
deleteBtn.onclick = () => this.confirmDeleteProperty(obj, k);
cardValueContainer.appendChild(deleteBtn);
}
content.appendChild(item);
}
card.appendChild(content);
return card;
}
isColorValue(value) {
const hexPattern = /^#([0-9A-F]{3}){1,2}([0-9A-F]{2})?$/i;
const rgbaPattern = /^rgba?\(\s*(\d{1,3}\s*,\s*){2}\d{1,3}\s*,?\s*(0|1|0?\.\d+|1?\.\d+)\s*\)$/;
return hexPattern.test(value) || rgbaPattern.test(value);
}
editKey(obj, oldKey) {
const newKey = prompt('Edit property name:', oldKey);
if (newKey && newKey !== oldKey) {
obj[newKey] = obj[oldKey];
delete obj[oldKey];
this.displayJSONCards();
}
this.notifyChange()
}
updateValue(obj, key, value, originalType) {
try {
// Handle different types based on the original value's type
switch (originalType) {
case 'string':
obj[key] = String(value);
break;
case 'number':
// If the original was a number, keep it number
if (!isNaN(value) || value === '') {
obj[key] = Number(value);
}
break;
case 'boolean':
obj[key] = value.toLowerCase() === 'true';
break;
default:
// Try to parse as JSON, fallback to string if it fails
try {
obj[key] = JSON.parse(value);
} catch {
obj[key] = value;
}
}
} catch {
obj[key] = value;
}
this.displayJSONCards();
this.notifyChange();
}
confirmDeleteProperty(obj, key) {
const confirmationDialog = document.getElementById('confirmationDialog');
confirmationDialog.showDialog(`Are you sure you need to delete: ${key}?`,
async () => {
this.deleteProperty(obj, key)
});
}
deleteProperty(obj, key) {
if (Array.isArray(obj)) {
obj.splice(key, 1);
} else {
delete obj[key];
}
this.displayJSONCards();
this.notifyChange()
}
addProperty(obj) {
if (Array.isArray(obj)) {
obj.push('');
} else {
const key = prompt('Enter new property name:');
if (key) {
obj[key] = '';
}
}
this.displayJSONCards();
this.notifyChange()
}
notifyChange() {
this.onChange(this.section, this.container)
}
}
export default SectionsFormManager;