Files

70 lines
2.8 KiB
JavaScript

export const $ = (selector, root = document) => root.querySelector(selector);
/** Create an element: h('button', { class: 'btn', text: 'Go', onclick }, ...children). */
export function h(tag, props = {}, ...children) {
const el = document.createElement(tag);
for (const [key, value] of Object.entries(props)) {
if (value == null || value === false) continue;
if (key === 'class') el.className = value;
else if (key === 'text') el.textContent = value;
else if (key === 'dataset') Object.assign(el.dataset, value);
else if (key.startsWith('on')) el.addEventListener(key.slice(2).toLowerCase(), value);
else el.setAttribute(key, value === true ? '' : value);
}
el.append(...children.flat().filter((c) => c != null && c !== false));
return el;
}
// ---------------------------------------------------------------------- icons
// Every icon is a grey square for now. When a theme provides images, list them
// in its theme.json ("assets": { "gen.gen1": "img/autoclipper.svg", ... }) and
// they replace the placeholders, with no change to the UI code.
let assetBase = '';
let assets = {};
export function setThemeAssets(themeId, themeAssets) {
assetBase = `themes/${themeId}/`;
assets = themeAssets ?? {};
}
/** key examples: "res.units", "gen.gen1", "upg.click_1" */
export function icon(key) {
const path = assets[key];
if (path) return h('img', { class: 'icon', src: assetBase + path, alt: '', loading: 'lazy' });
return h('span', { class: 'icon icon--placeholder', 'aria-hidden': 'true', dataset: { icon: key } });
}
/** Re-resolve the icons that live in index.html (the resource bar). */
export function refreshStaticIcons(root = document) {
for (const el of root.querySelectorAll('.resourcebar [data-icon]')) el.replaceWith(icon(el.dataset.icon));
}
// ------------------------------------------------------------ buyable blocks
// A block you click to buy (instead of a button inside it): it is announced as
// a button, reachable with the keyboard (Enter / Space), outlined when hovered
// and greyed out when it cannot be bought.
/** Make `el` act as one big button. `activate(event)` runs on click, Enter or Space. */
export function makeBuyable(el, activate) {
el.classList.add('card--buy');
el.setAttribute('role', 'button');
el.tabIndex = 0;
const allowed = () => el.getAttribute('aria-disabled') !== 'true';
el.addEventListener('click', (event) => {
if (allowed()) activate(event);
});
el.addEventListener('keydown', (event) => {
if ((event.key === 'Enter' || event.key === ' ') && event.target === el) {
event.preventDefault();
if (allowed()) activate(event);
}
});
}
/** Show whether a buyable block can currently be bought. */
export function setBuyable(el, canBuy) {
el.classList.toggle('is-disabled', !canBuy);
el.setAttribute('aria-disabled', String(!canBuy));
}