158 lines
7.1 KiB
JavaScript
158 lines
7.1 KiB
JavaScript
/**
|
|
* Equipment Rendering Module
|
|
* Handles UI rendering for the equipment grid and item creation
|
|
*/
|
|
|
|
import { extensionSettings, $equipmentContainer } from '../../core/state.js';
|
|
import { i18n } from '../../core/i18n.js';
|
|
import { EQUIPMENT_CATEGORIES, SLOTS_LIST, escapeHtml } from '../equipment/constants.js';
|
|
|
|
/**
|
|
* Renders a single equipment slot
|
|
* @param {Object} slotDef - Slot definition from SLOTS_LIST
|
|
* @param {Object|null} item - The equipped item or null
|
|
* @returns {string} HTML for the slot
|
|
*/
|
|
function renderSlot(slotDef, item) {
|
|
const slotId = slotDef.id;
|
|
const slotName = i18n.getTranslation(`equipment.slots.${slotId}`) || slotId.replace(/(\d+)/, ' $1');
|
|
const equippedClass = item ? 'equipped' : '';
|
|
|
|
if (item) {
|
|
const statsText = Object.entries(item.stats || {})
|
|
.filter(([_, val]) => val > 0)
|
|
.map(([key, val]) => `<span class="rpg-eq-stat"><span class="rpg-eq-stat-label">${escapeHtml(key.toUpperCase())}</span><span class="rpg-eq-stat-value">+${val}</span></span>`)
|
|
.join('');
|
|
|
|
return `
|
|
<div class="rpg-equipment-slot ${equippedClass}" data-slot="${slotId}" data-item-id="${item.id}">
|
|
<div class="rpg-equipment-slot-header">
|
|
<i class="fa-solid ${slotDef.icon}"></i>
|
|
<span class="rpg-equipment-slot-name">${escapeHtml(slotName)}</span>
|
|
<button class="rpg-equipment-unequip-btn" data-action="unequip" title="${i18n.getTranslation('equipment.unequip') || 'Unequip'}">
|
|
<i class="fa-solid fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<div class="rpg-equipment-item-name">${escapeHtml(item.name)}</div>
|
|
${statsText ? `<div class="rpg-equipment-stats">${statsText}</div>` : ''}
|
|
${item.description ? `<div class="rpg-equipment-description">${escapeHtml(item.description)}</div>` : ''}
|
|
<div class="rpg-equipment-item-actions">
|
|
<button class="rpg-equipment-edit-btn" data-action="edit-item" data-item-id="${item.id}" title="${i18n.getTranslation('equipment.editItem') || 'Edit item'}">
|
|
<i class="fa-solid fa-pen"></i>
|
|
</button>
|
|
<button class="rpg-equipment-delete-btn" data-action="delete-item" data-item-id="${item.id}" title="${i18n.getTranslation('equipment.deleteItem') || 'Delete item'}">
|
|
<i class="fa-solid fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
return `
|
|
<div class="rpg-equipment-slot" data-slot="${slotId}">
|
|
<div class="rpg-equipment-slot-header">
|
|
<i class="fa-solid ${slotDef.icon}"></i>
|
|
<span class="rpg-equipment-slot-name">${escapeHtml(slotName)}</span>
|
|
</div>
|
|
<div class="rpg-equipment-empty">${i18n.getTranslation('equipment.emptySlot') || 'Empty'}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Generates the full equipment section HTML
|
|
* @returns {string} Complete HTML for the equipment section
|
|
*/
|
|
function generateEquipmentHTML() {
|
|
const equipment = extensionSettings.userStats?.equipment || { items: [], slots: {} };
|
|
const slots = equipment.slots || {};
|
|
const items = equipment.items || [];
|
|
|
|
let html = '<div class="rpg-equipment-container">';
|
|
|
|
// Header with add button
|
|
html += `
|
|
<div class="rpg-equipment-header">
|
|
<h3>
|
|
<i class="fa-solid fa-shield-halved"></i>
|
|
<span data-i18n-key="equipment.title">${i18n.getTranslation('equipment.title') || 'Equipment'}</span>
|
|
</h3>
|
|
<button class="rpg-equipment-add-btn" data-action="show-create-modal" title="${i18n.getTranslation('equipment.createItem') || 'Create new equipment'}">
|
|
<i class="fa-solid fa-plus"></i> ${i18n.getTranslation('equipment.createItem') || 'Create Equipment'}
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
// Equipment grid
|
|
html += '<div class="rpg-equipment-grid">';
|
|
|
|
// Render each slot
|
|
for (const slotDef of SLOTS_LIST) {
|
|
const item = items.find(i => i.slot === slotDef.id);
|
|
html += renderSlot(slotDef, item);
|
|
}
|
|
|
|
html += '</div>';
|
|
|
|
// Inventory list (items not currently equipped)
|
|
const unequipped = items.filter(item => !item.slot);
|
|
if (unequipped.length > 0) {
|
|
html += '<div class="rpg-equipment-inventory">';
|
|
html += `<h4>${i18n.getTranslation('equipment.inventoryTitle') || 'Inventory'}</h4>`;
|
|
html += '<div class="rpg-equipment-inventory-list">';
|
|
|
|
for (const item of unequipped) {
|
|
const category = EQUIPMENT_CATEGORIES[item.type];
|
|
const statsText = Object.entries(item.stats || {})
|
|
.filter(([_, val]) => val > 0)
|
|
.map(([key, val]) => `<span class="rpg-eq-stat"><span class="rpg-eq-stat-label">${escapeHtml(key.toUpperCase())}</span><span class="rpg-eq-stat-value">+${val}</span></span>`)
|
|
.join('');
|
|
|
|
html += `
|
|
<div class="rpg-equipment-inventory-item" data-item-id="${item.id}">
|
|
<div class="rpg-equipment-inventory-item-header">
|
|
<i class="fa-solid ${category ? category.icon : 'fa-circle'}"></i>
|
|
<span class="rpg-equipment-inventory-item-name">${escapeHtml(item.name)}</span>
|
|
<span class="rpg-equipment-inventory-item-type">${category ? (i18n.getTranslation(`equipment.types.${item.type}`) || item.type) : escapeHtml(item.type)}</span>
|
|
</div>
|
|
${statsText ? `<div class="rpg-equipment-stats">${statsText}</div>` : ''}
|
|
<div class="rpg-equipment-item-actions">
|
|
<button class="rpg-equipment-equip-btn" data-action="equip" data-item-id="${item.id}" title="${i18n.getTranslation('equipment.equip') || 'Equip'}">
|
|
<i class="fa-solid fa-hand"></i>
|
|
</button>
|
|
<button class="rpg-equipment-edit-btn" data-action="edit-item" data-item-id="${item.id}" title="${i18n.getTranslation('equipment.editItem') || 'Edit item'}">
|
|
<i class="fa-solid fa-pen"></i>
|
|
</button>
|
|
<button class="rpg-equipment-delete-btn" data-action="delete-item" data-item-id="${item.id}" title="${i18n.getTranslation('equipment.deleteItem') || 'Delete item'}">
|
|
<i class="fa-solid fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
html += '</div>';
|
|
html += '</div>';
|
|
}
|
|
|
|
html += '</div>';
|
|
|
|
return html;
|
|
}
|
|
|
|
/**
|
|
* Main equipment rendering function
|
|
* Gets data from state/settings and updates DOM directly.
|
|
*/
|
|
export function renderEquipment() {
|
|
if (!$equipmentContainer || !extensionSettings.showEquipment) {
|
|
return;
|
|
}
|
|
|
|
const html = generateEquipmentHTML();
|
|
$equipmentContainer.html(html);
|
|
|
|
// Re-apply translations
|
|
i18n.applyTranslations($equipmentContainer[0]);
|
|
}
|