worldmap-viewer

git clone https://git.godosa.eu/worldmap-viewer

master

raw · 1449 bytes

// ui.js — small DOM helpers (text only via append/textContent, never innerHTML)
export const $ = (sel, root = document) => root.querySelector(sel);

const SVG_NS = 'http://www.w3.org/2000/svg';
export const el = (tag, attrs = {}, ...kids) => fill(document.createElement(tag), attrs, kids);
export const svg = (tag, attrs = {}, ...kids) => fill(document.createElementNS(SVG_NS, tag), attrs, kids);

function fill(e, attrs, kids) {
  for (const [k, v] of Object.entries(attrs)) {
    if (typeof v === 'function' && k.startsWith('on')) e.addEventListener(k.slice(2), v);
    else if (v !== undefined && v !== null && v !== false) e.setAttribute(k, v === true ? '' : String(v));
  }
  for (const k of kids.flat()) if (k !== undefined && k !== null && k !== false) e.append(k);
  return e;
}

let toastTimer;
export function toast(msg, kind = 'error') {
  const t = $('#toast');
  t.textContent = msg;
  t.className = `show ${kind}`;
  clearTimeout(toastTimer);
  toastTimer = setTimeout(() => { t.className = ''; }, 5000);
}

export async function api(url, { method = 'GET', body } = {}) {
  const r = await fetch(url, { method, headers: body === undefined ? {} : { 'Content-Type': 'application/json' },
    body: body === undefined ? undefined : JSON.stringify(body) });
  if (r.status === 204) return null;
  const data = await r.json().catch(() => ({}));
  if (!r.ok) throw new Error(data.error || `${r.status} ${r.statusText}`);
  return data;
}