Skip to content

Commit

Permalink
update javascript to allow nested key/value pairs
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-nork committed Oct 16, 2023
1 parent 2c8480a commit fef7b59
Showing 1 changed file with 124 additions and 101 deletions.
225 changes: 124 additions & 101 deletions chirps/asset/static/js/key_value_widget.js
Original file line number Diff line number Diff line change
@@ -1,101 +1,124 @@
document.addEventListener('DOMContentLoaded', () => {
const keyValueContainers = document.querySelectorAll('.key-value-container');

keyValueContainers.forEach((container, index) => {
const addButton = container.nextElementSibling;

// Add a data attribute to the button to prevent multiple event listeners
if (!addButton.hasAttribute('data-initialized')) {
addButton.setAttribute('data-initialized', 'true');

addButton.addEventListener('click', () => {
addKeyValuePair(container);
});

// Initialize with existing pairs from the hidden input value
const hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
if (hiddenInput.value) {
const pairs = JSON.parse(hiddenInput.value);
for (const key in pairs) {
addKeyValuePair(container, key, pairs[key]);
}
}
}
});
});



function addKeyValuePair(container, key = '', value = '') {
if (!container) {
console.error('Container not found');
return;
}
const keyInput = document.createElement('input');
keyInput.type = 'text';
keyInput.placeholder = 'Key';
keyInput.value = key;
keyInput.className = 'form-control mr-2';

const valueInput = document.createElement('input');
valueInput.type = 'text';
valueInput.placeholder = 'Value';
valueInput.value = value;
valueInput.className = 'form-control mr-2';

const pair = document.createElement('div');
pair.className = 'form-inline mb-2';
pair.appendChild(keyInput);
pair.appendChild(valueInput);

// Add tooltip icon and message for the 'body' field with key 'data'
if (container.id.includes('body') && value === '%query%') {
const tooltipIcon = document.createElement('i');
tooltipIcon.className = 'fas fa-info-circle ml-2';
tooltipIcon.setAttribute('data-toggle', 'tooltip');
tooltipIcon.setAttribute('title', 'Chirps sends a POST request to the API endpoint.\n\nThe value %query% will be replaced by the request text.\n\nPlease update the key to match what is expected in the request.');

pair.appendChild(tooltipIcon);

// this value will be used to know where in the request to put our message, so don't let the user change it
valueInput.disabled = true;
}

if (value !== '%query%') {
const removeButton = document.createElement('button');
removeButton.type = 'button';
removeButton.textContent = 'Remove';
removeButton.className = 'btn btn-danger';

pair.appendChild(removeButton);

removeButton.addEventListener('click', () => {
container.removeChild(pair);
updateHiddenInput(container);
});
}

container.appendChild(pair);

keyInput.addEventListener('input', () => {
updateHiddenInput(container);
});

valueInput.addEventListener('input', () => {
updateHiddenInput(container);
});
}

function updateHiddenInput(container) {
const pairs = {};
const keyValuePairs = container.querySelectorAll('div.form-inline');

keyValuePairs.forEach(pair => {
const keyInput = pair.querySelector('input:first-child');
const valueInput = pair.querySelector('input:nth-child(2)');
pairs[keyInput.value] = valueInput.value;
});

const hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
hiddenInput.value = JSON.stringify(pairs);
}
document.addEventListener('DOMContentLoaded', () => {
const keyValueContainers = document.querySelectorAll('.key-value-container');

keyValueContainers.forEach((container, index) => {
const addButton = container.nextElementSibling;

// Add a data attribute to the button to prevent multiple event listeners
if (!addButton.hasAttribute('data-initialized')) {
addButton.setAttribute('data-initialized', 'true');

addButton.addEventListener('click', () => {
addKeyValuePair(container);
});

// Initialize with existing pairs from the hidden input value
const hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
if (hiddenInput.value) {
const pairs = JSON.parse(hiddenInput.value);
for (const key in pairs) {
addKeyValuePair(container, key, pairs[key]);
}
}
}
});
});

function addKeyValuePair(container, key = '', value = '', parentPair = null) {
if (!container) {
console.error('Container not found');
return;
}
const keyInput = document.createElement('input');
keyInput.type = 'text';
keyInput.placeholder = 'Key';
keyInput.value = key;
keyInput.className = 'form-control mr-2 key-input';

const valueInput = document.createElement('input');
valueInput.type = 'text';
valueInput.placeholder = 'Value';
valueInput.value = value;
valueInput.className = 'form-control mr-2 value-input';

const addButton = document.createElement('button');
addButton.type = 'button';
addButton.textContent = 'Add Nested Key-Value Pair';
addButton.className = 'btn btn-secondary add-nested-key-value-pair';

const removeButton = document.createElement('button');
removeButton.type = 'button';
removeButton.textContent = 'Remove';
removeButton.className = 'btn btn-danger remove-key-value-pair ml-2';

const pair = document.createElement('div');
pair.className = 'form-inline mb-2 key-value-input';
pair.appendChild(keyInput);
pair.appendChild(valueInput);
pair.appendChild(addButton);
pair.appendChild(removeButton);

if (parentPair) {
parentPair.appendChild(pair);
pair.dataset.parentKey = parentPair.querySelector('.key-input').value;
pair.classList.add('nested-key-value');

// Add margin-left to visually show the nesting
pair.style.marginLeft = '20px';

// Remove the value field of the parent pair
const parentValueInput = parentPair.querySelector('.value-input');
parentPair.removeChild(parentValueInput);
} else {
container.appendChild(pair);
}

keyInput.addEventListener('input', () => {
updateHiddenInput(container);
});

valueInput.addEventListener('input', () => {
updateHiddenInput(container);
});

addButton.addEventListener('click', () => {
addKeyValuePair(container, '', '', pair);
});

removeButton.addEventListener('click', () => {
(parentPair || container).removeChild(pair);
updateHiddenInput(container);
});
}

function updateHiddenInput(container) {
function processKeyValuePairs(parentNode, parentKey = '') {
const keyValuePairs = parentNode.querySelectorAll(
parentKey ? `.key-value-input[data-parent-key="${parentKey}"]` : '.key-value-input:not([data-parent-key])'
);

const pairs = {};

keyValuePairs.forEach(pair => {
const keyInput = pair.querySelector('.key-input');
const valueInput = pair.querySelector('.value-input');

if (keyInput.value && valueInput.value) {
pairs[keyInput.value] = valueInput.value;
const nestedPairs = processKeyValuePairs(pair, keyInput.value);

if (Object.keys(nestedPairs).length > 0) {
pairs[keyInput.value] = nestedPairs;
}
}
});

return pairs;
}

const pairs = processKeyValuePairs(container);

const hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
hiddenInput.value = JSON.stringify(pairs);
}

0 comments on commit fef7b59

Please sign in to comment.