/**
* Класс для управления коллекцией сущностей (добавление/удаление элементов).
* Требуется jQuery.
*/
class EntityCollection {
/**
* @param {HTMLElement|jQuery} element Корневой элемент коллекции
*/
constructor(element) {
this.collectionName = $(element).data('collection-name') || null;
/** @private */
this.$element = $(element);
/** @private */
this.$itemsContainer = this.$element.find('.js-entity-collection-items[data-collection-name="' + this.collectionName + '"]');
/** @private */
this.prototypeName = this.$element.data('prototype-name') || '__name__';
/** @private {string|null} */
this.prototypeHtml = this.$element.data('prototype') || null;
/** @private {string} */
this.itemClass = this.$element.data('item-class') || 'entity-collection__item';
/** @private {number} */
this.index = this.$itemsContainer.find('.' + this.itemClass).length || this.$itemsContainer.children().length || 0;
this.init();
}
/**
* Инициализация обработчиков событий.
* @private
*/
init() {
// Добавление элемента
this.$element.on('click', '.js-entity-collection-add', (event) => {
const $button = $(event.currentTarget);
const btnName = $button.data('collection-name');
const containerName = this.$element.data('collection-name');
// Если кнопка явно привязана к другой коллекции — игнорируем клик
if (btnName != null && String(btnName) !== String(containerName)) {
return;
}
this.addItem();
});
// Удаление элемента — поддерживаем старые и новые селекторы кнопки удаления
this.$element.on('click', '.js-entity-collection-remove', (event) => {
this.removeItem(event);
});
this.updateItemsHeaders();
}
/**
* Удаляет элемент коллекции, родственный элементу-селектору удаления.
* @param {Event} event
*/
removeItem(event) {
const $button = $(event.currentTarget);
const $item = $button.closest('.' + this.itemClass);
if ($item.length) {
$item.remove();
this.updateItemsHeaders();
}
}
/**
* Добавляет новый элемент в коллекцию, используя prototypeHtml.
*/
addItem() {
if (!this.prototypeHtml) {
// Используем console.warn — это не должно ломать выполнение
console.warn('EntityCollection: prototypeHtml is missing');
return;
}
const newForm = this.prototypeHtml.replace(new RegExp(this.prototypeName, 'g'), this.index);
const $wrapper = $('<div></div>').addClass(this.itemClass + ' mb-3 p-3 border rounded');
$wrapper.html(newForm);
this.$itemsContainer.append($wrapper);
this.index += 1;
this.updateItemsHeaders();
}
updateItemsHeaders() {
this.$itemsContainer.find('.entity-collection-item-header[data-collection-name="'
+ this.collectionName + '"]').each((index, header) => {
let text = $(header).data('header-text');
if (text) {
text = text.replaceAll('{itemIndex}', (index + 1));
$(header).html(text);
}
});
}
}