Another file converted

This commit is contained in:
Alex Janousek
2025-10-23 12:04:26 -04:00
parent 2fdb0e30f9
commit cedfd4fe04

View File

@@ -1,22 +1,28 @@
(function (window) { (function (window) {
'use strict'; 'use strict';
// This module creates dynamic add/remove input lists. It lets users add multiple entries
// (like email addresses or domains) with "Add another" and "Remove" buttons. It handles
// min/max entry limits, focus management, and preserves input attributes.
// We're keeping this for now but it's not currently being used in the application.
var Modules = window.NotifyModules; var Modules = window.NotifyModules;
var lists = [], var lists = [],
listEntry, listEntry,
ListEntry; ListEntry;
ListEntry = function (elm) { ListEntry = function (elm) {
var $elm = $(elm), var idPattern = elm.id;
idPattern = $elm.prop('id');
if (!idPattern) { return false; } if (!idPattern) {
return false;
}
this.idPattern = idPattern; this.idPattern = idPattern;
this.elementSelector = '.list-entry, .input-list__button--remove, .input-list__button--add'; this.elementSelector = '.list-entry, .input-list__button--remove, .input-list__button--add';
this.entries = []; this.entries = [];
this.$wrapper = $elm; this.wrapper = elm;
this.minEntries = 2; this.minEntries = 2;
this.listItemName = this.$wrapper.data('listItemName'); this.listItemName = this.wrapper.dataset.listItemName;
this.getSharedAttributes(); this.getSharedAttributes();
this.getOriginalClasses(); this.getOriginalClasses();
@@ -26,33 +32,43 @@
this.render(); this.render();
this.bindEvents(); this.bindEvents();
}; };
ListEntry.optionalAttributes = ['aria-describedby']; ListEntry.escapeHtml = function(unsafe) {
if (!unsafe) return '';
return String(unsafe)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
};
ListEntry.prototype.renderEntry = function(data) { ListEntry.prototype.renderEntry = function(data) {
var escapeHtml = ListEntry.escapeHtml;
return ` return `
<div class="list-entry"> <div class="list-entry">
<label for="${data.id}" class="usa-label"> <label for="${escapeHtml(data.id)}" class="usa-label">
<span class="usa-sr-only">${data.listItemName} number </span>${data.number}. <span class="usa-sr-only">${escapeHtml(data.listItemName)} number </span>${escapeHtml(data.number)}.
</label> </label>
<input <input
class="usa-input ${data.classes || ''}" class="usa-input ${escapeHtml(data.classes || '')}"
name="${data.name}" name="${escapeHtml(data.name)}"
id="${data.id}" id="${escapeHtml(data.id)}"
${data.value ? `value="${data.value}"` : ''} ${data.value ? `value="${escapeHtml(data.value)}"` : ''}
${data.sharedAttributes} ${data.sharedAttributes}
/> />
${data.button ? ` ${data.button ? `
<button type="button" class="usa-button usa-button--unstyled input-list__button--remove"> <button type="button" class="usa-button usa-button--unstyled input-list__button--remove">
Remove<span class="usa-sr-only"> ${data.listItemName} number ${data.number}</span> Remove<span class="usa-sr-only"> ${escapeHtml(data.listItemName)} number ${escapeHtml(data.number)}</span>
</button> </button>
` : ''} ` : ''}
</div> </div>
`; `;
}; };
ListEntry.prototype.renderAddButton = function(data) { ListEntry.prototype.renderAddButton = function(data) {
return `<button type="button" class="usa-button usa-button--outline input-list__button--add margin-top-4">Add another ${data.listItemName} (${data.entriesLeft} remaining)</button>`; var escapeHtml = ListEntry.escapeHtml;
return `<button type="button" class="usa-button usa-button--outline input-list__button--add margin-top-4">Add another ${escapeHtml(data.listItemName)} (${escapeHtml(data.entriesLeft)} remaining)</button>`;
}; };
ListEntry.prototype.getSharedAttributes = function () { ListEntry.prototype.getSharedAttributes = function () {
var $inputs = this.$wrapper.find('input'), var inputs = this.wrapper.querySelectorAll('input'),
generatedAttributes = ['id', 'name', 'value', 'class'], generatedAttributes = ['id', 'name', 'value', 'class'],
attributes = [], attributes = [],
attrIdx, attrIdx,
@@ -64,15 +80,16 @@
elmIdx = attrsByElm.length, elmIdx = attrsByElm.length,
existingAttributes = [], existingAttributes = [],
elmAttrs, elmAttrs,
attrIdx; attrIdx,
escapeHtml = ListEntry.escapeHtml;
while (elmIdx--) { while (elmIdx--) {
elmAttrs = attrsByElm[elmIdx]; elmAttrs = attrsByElm[elmIdx];
attrIdx = elmAttrs.length; attrIdx = elmAttrs.length;
while (attrIdx--) { while (attrIdx--) {
// prevent duplicates // prevent duplicates
if ($.inArray(elmAttrs[attrIdx].name, existingAttributes) === -1) { if (existingAttributes.indexOf(elmAttrs[attrIdx].name) === -1) {
attrStr += ` ${elmAttrs[attrIdx].name}="${elmAttrs[attrIdx].value}"`; attrStr += ` ${escapeHtml(elmAttrs[attrIdx].name)}="${escapeHtml(elmAttrs[attrIdx].value)}"`;
existingAttributes.push(elmAttrs[attrIdx].name); existingAttributes.push(elmAttrs[attrIdx].name);
} }
} }
@@ -80,11 +97,11 @@
return attrStr; return attrStr;
}; };
$inputs.each(function (idx, elm) { inputs.forEach(function (elm) {
attrIdx = elm.attributes.length; attrIdx = elm.attributes.length;
elmAttributes = []; elmAttributes = [];
while(attrIdx--) { while(attrIdx--) {
if ($.inArray(elm.attributes[attrIdx].name, generatedAttributes) === -1) { if (generatedAttributes.indexOf(elm.attributes[attrIdx].name) === -1) {
elmAttributes.push({ elmAttributes.push({
'name': elm.attributes[attrIdx].name, 'name': elm.attributes[attrIdx].name,
'value': elm.attributes[attrIdx].value 'value': elm.attributes[attrIdx].value
@@ -99,9 +116,9 @@
this.sharedAttributes = (attributes.length) ? getAttributesHTML(attributes) : ''; this.sharedAttributes = (attributes.length) ? getAttributesHTML(attributes) : '';
}; };
ListEntry.prototype.getOriginalClasses = function () { ListEntry.prototype.getOriginalClasses = function () {
var $firstInput = this.$wrapper.find('input').first(); var firstInput = this.wrapper.querySelector('input');
if ($firstInput.length) { if (firstInput) {
var classList = $firstInput.attr('class'); var classList = firstInput.getAttribute('class');
if (classList) { if (classList) {
// Preserve any additional classes from the original input // Preserve any additional classes from the original input
this.additionalClasses = classList; this.additionalClasses = classList;
@@ -114,8 +131,8 @@
}; };
ListEntry.prototype.getValues = function () { ListEntry.prototype.getValues = function () {
this.entries = []; this.entries = [];
this.$wrapper.find('input').each(function (idx, elm) { this.wrapper.querySelectorAll('input').forEach(function (elm) {
var val = $(elm).val(); var val = elm.value;
this.entries.push(val); this.entries.push(val);
}.bind(this)); }.bind(this));
@@ -144,11 +161,12 @@
} }
}; };
ListEntry.prototype.bindEvents = function () { ListEntry.prototype.bindEvents = function () {
this.$wrapper.on('click', '.input-list__button--remove', function (e) { this.wrapper.addEventListener('click', function (e) {
this.removeEntry($(e.target)); if (e.target.closest('.input-list__button--remove')) {
}.bind(this)); this.removeEntry(e.target);
this.$wrapper.on('click', '.input-list__button--add', function (e) { } else if (e.target.closest('.input-list__button--add')) {
this.addEntry(); this.addEntry();
}
}.bind(this)); }.bind(this));
}; };
ListEntry.prototype.shiftFocus = function (opts) { ListEntry.prototype.shiftFocus = function (opts) {
@@ -159,7 +177,13 @@
} else { // opts.action === 'add' } else { // opts.action === 'add'
numberTargeted = opts.entryNumberFocused + 1; numberTargeted = opts.entryNumberFocused + 1;
} }
this.$wrapper.find('.list-entry').eq(numberTargeted - 1).find('input').focus(); var entries = this.wrapper.querySelectorAll('.list-entry');
if (entries[numberTargeted - 1]) {
var input = entries[numberTargeted - 1].querySelector('input');
if (input) {
input.focus();
}
}
}; };
ListEntry.prototype.removeEntryFromEntries = function (entryNumber) { ListEntry.prototype.removeEntryFromEntries = function (entryNumber) {
var idx, var idx,
@@ -173,7 +197,7 @@
} }
this.entries = newEntries; this.entries = newEntries;
}; };
ListEntry.prototype.addEntry = function ($removeButton) { ListEntry.prototype.addEntry = function () {
var currentLastEntryNumber = this.entries.length; var currentLastEntryNumber = this.entries.length;
this.getValues(); this.getValues();
@@ -181,8 +205,23 @@
this.render(); this.render();
this.shiftFocus({ 'action' : 'add', 'entryNumberFocused' : currentLastEntryNumber }); this.shiftFocus({ 'action' : 'add', 'entryNumberFocused' : currentLastEntryNumber });
}; };
ListEntry.prototype.removeEntry = function ($removeButton) { ListEntry.prototype.removeEntry = function (removeButton) {
var entryNumber = parseInt($removeButton.find('span').text().match(/\d+/)[0], 10); var button = removeButton.closest('.input-list__button--remove');
if (!button) {
console.error('ListEntry: Remove button not found');
return;
}
var span = button.querySelector('span');
if (!span) {
console.error('ListEntry: Entry number span not found in remove button');
return;
}
var match = span.textContent.match(/\d+/);
if (!match) {
console.error('ListEntry: Could not find entry number in remove button');
return;
}
var entryNumber = parseInt(match[0], 10);
this.getValues(); this.getValues();
this.removeEntryFromEntries(entryNumber); this.removeEntryFromEntries(entryNumber);
@@ -190,8 +229,10 @@
this.shiftFocus({ 'action' : 'remove', 'entryNumberFocused' : entryNumber }); this.shiftFocus({ 'action' : 'remove', 'entryNumberFocused' : entryNumber });
}; };
ListEntry.prototype.render = function () { ListEntry.prototype.render = function () {
this.$wrapper.find(this.elementSelector).remove(); this.wrapper.querySelectorAll(this.elementSelector).forEach(function(el) {
$.each(this.entries, function (idx, entry) { el.remove();
});
this.entries.forEach(function (entry, idx) {
var entryNumber = idx + 1, var entryNumber = idx + 1,
dataObj = { dataObj = {
'id' : this.getId(entryNumber), 'id' : this.getId(entryNumber),
@@ -207,10 +248,10 @@
if (entryNumber > 1) { if (entryNumber > 1) {
dataObj.button = true; dataObj.button = true;
} }
this.$wrapper.append(this.renderEntry(dataObj)); this.wrapper.insertAdjacentHTML('beforeend', this.renderEntry(dataObj));
}.bind(this)); }.bind(this));
if (this.entries.length < this.maxEntries) { if (this.entries.length < this.maxEntries) {
this.$wrapper.append(this.renderAddButton({ this.wrapper.insertAdjacentHTML('beforeend', this.renderAddButton({
'listItemName' : this.listItemName, 'listItemName' : this.listItemName,
'entriesLeft' : (this.maxEntries - this.entries.length) 'entriesLeft' : (this.maxEntries - this.entries.length)
})); }));
@@ -219,7 +260,7 @@
Modules['list-entry'] = function () { Modules['list-entry'] = function () {
this.start = component => lists.push(new ListEntry($(component))); this.start = component => lists.push(new ListEntry(component));
}; };