let resolveLoaded; const loaded = new Promise(resolve => resolveLoaded = resolve); function e(unsafeText) { let div = document.createElement('div'); div.innerText = unsafeText; return div.innerHTML; } /** * @type {import('../../src/lib/rpg/items').Item | null} */ let draggedItem = null; /** * @type {Record} */ let itemLists = {}; function listToString(list) { return list.map(stack => `${stack.item.id},${stack.quantity}`).join(';'); } function updateString() { document.querySelector('#recipe-string').innerText = [ listToString(itemLists['inputs'] || []), listToString(itemLists['requirements'] || []), listToString(itemLists['outputs'] || []), ].join('|'); } /** * @param {import('../../src/lib/rpg/items').Item} item */ function renderItem(item) { const i = document.createElement('div'); i.innerHTML = `
${e(item.emoji)}
${e(item.name)}
${item.description ? e(item.description) : 'No description'}
`; i.classList.add('item'); i.draggable = true; i.addEventListener('dragstart', event => { draggedItem = item; event.target.classList.add('dragging'); }); i.addEventListener('dragend', event => { draggedItem = null; event.target.classList.remove('dragging'); }); return i; } function renderItemStack(item, quantity, type) { const i = document.createElement('div'); i.innerHTML = `
${e(item.emoji)}
x${quantity}
`; i.classList.add('itemstack'); i.draggable = true; i.addEventListener('dragstart', event => { event.target.classList.add('dragging'); }); i.addEventListener('dragend', event => { event.target.classList.remove('dragging'); itemLists[type] = itemLists[type] || []; const items = itemLists[type]; const stackIdx = items.findIndex(n => n.item.id === item.id); if (stackIdx !== -1) items.splice(stackIdx, 1); document.querySelector(`.item-list[data-type="${type}"]`).replaceWith(renderItemList(items, type)); updateString(); }); return i; } function renderItemList(items, type) { const i = document.createElement('div'); i.textContent = ''; items.forEach(itemStack => { i.appendChild(renderItemStack(itemStack.item, itemStack.quantity, type)); }); i.dataset.type = type; i.classList.add('item-list'); // prevent default to allow drop i.addEventListener('dragover', (event) => event.preventDefault(), false); i.addEventListener('dragenter', event => draggedItem && event.target.classList.add('dropping')); i.addEventListener('dragleave', event => draggedItem && event.target.classList.remove('dropping')); i.addEventListener('drop', (event) => { event.preventDefault(); event.target.classList.remove('dropping'); if (!draggedItem) return; itemLists[type] = itemLists[type] || []; const items = itemLists[type]; const itemStack = items.find(v => v.item.id === draggedItem.id); if (!itemStack) { items.push({ item: draggedItem, quantity: 1 }); } else { itemStack.quantity = itemStack.quantity + 1; } updateString(); draggedItem = null; event.target.replaceWith(renderItemList(items, type)); }); return i; } Promise.all([ fetch(`/api/items${document.location.search}`) .then(res => res.json()), loaded ]).then(([items]) => { const itemsContainer = document.querySelector('.items'); itemsContainer.textContent = ''; items.forEach(item => itemsContainer.appendChild(renderItem(item)) ); document.querySelectorAll('.item-list').forEach(elem => { const type = elem.dataset.type; elem.replaceWith(renderItemList([], type)); }); updateString(); }); document.addEventListener('DOMContentLoaded', () => resolveLoaded());