Files

148 lines
6.5 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:a22e862b7b1defbcc74c1844981b5027dbd831d7e549f1dff573576ab6b2f26e
import { t, locale } from '../i18n/index.js';
import { ROUTE_KEYS, TARGETS, PLACE_LABELS, targetHash } from '../../shared/config.js';
import { GUIDE_SECTIONS, GUIDE_GROUPS, splitPlaceTokens, guideText } from '../../shared/guide.js';
import { create as createTabs } from '../ui/tabs.js';
import { esc } from '../ui/escape.js';
import { CHEVRON_RIGHT, ADJUST } from '../ui/icons.js';
import { GUIDE_FIGURE_DRAWINGS } from '../ui/guide-figures.js';
import { placeAvailable } from '../routes.js';
export const title = () => t('guide.title');
export const headerInColumn = true;
const ROUTE = ROUTE_KEYS.GUIDE;
const IDS = GUIDE_SECTIONS.map(s => s.id);
const BY_ID = new Map(GUIDE_SECTIONS.map(s => [s.id, s]));
const GROUP_OF = new Map(GUIDE_GROUPS.flatMap(g => g.sections.map(id => [id, g.id])));
const texts = () => guideText(locale());
const titleOf = id => texts().sections[id].title;
const shown = id => { const p = BY_ID.get(id)?.place; return !p || placeAvailable(TARGETS[p]); };
const parseHash = () => (location.hash || '').replace(/^#\/?/, '').split('/')[1] || null;
const hashFor = id => targetHash({ route: ROUTE, tab: id });
const segLabel = id => { try { return t(PLACE_LABELS[id] || `nav.${id}`); } catch { return id; } };
const segsOf = key => { const { route, tab, sub } = TARGETS[key]; return [route, tab, sub].filter(Boolean); };
const placeName = key => segLabel(segsOf(key).at(-1));
function placeNameUnique(key) {
const naam = placeName(key);
const dubbel = Object.keys(TARGETS).some(k => k !== key && placeName(k) === naam);
if (!dubbel) return naam;
const segs = segsOf(key);
return `${segLabel(segs.at(-2) ?? segs[0])} › ${naam}`;
}
const placeLink = (key, label, cls = '', after = '') =>
`<a${cls ? ` class="${cls}"` : ''} href="${esc(targetHash(TARGETS[key]))}" target="_blank" rel="noopener" `
+ `title="${esc(t('guide.newTab'))}">${esc(label)}${after}</a>`;
const ICONS = Object.freeze({ adjust: ADJUST });
function inline(text) {
return splitPlaceTokens(text).map(part => {
if (typeof part === 'string') return esc(part);
if (part.icon) return ICONS[part.icon] ? `<span class="guide-icon">${ICONS[part.icon]}</span>` : '';
return placeAvailable(TARGETS[part.place]) ? placeLink(part.place, placeName(part.place)) : esc(placeName(part.place));
}).join('');
}
const lead = (term, text) => `<strong>${esc(term)}</strong> ${inline(text)}`;
function block(b) {
if (typeof b === 'string') return `<p>${inline(b)}</p>`;
if (b.heading) return `<h3 class="guide-h">${esc(b.heading)}</h3>`;
if (b.code) return `<pre class="guide-code"><code>${esc(b.code)}</code></pre>`;
if (b.note) return `<div class="guide-note">${inline(b.note)}</div>`;
if (b.figure) {
const draw = GUIDE_FIGURE_DRAWINGS[b.figure];
return draw ? `<figure style="margin:0.4rem 0 1.2rem">`
+ `<div style="overflow-x:auto;padding:0.8rem;border:1px solid var(--border-mid);border-radius:var(--radius-sm)">${draw(b.caption)}</div>`
+ `<figcaption style="margin-top:0.4rem;font-size:0.85rem;color:var(--text-sec)">${inline(b.caption)}</figcaption></figure>` : '';
}
if (b.list && b.list.every(Array.isArray)) {
return `<dl class="guide-terms">${b.list.map(([term, text]) => `<dt>${esc(term)}</dt><dd>${inline(text)}</dd>`).join('')}</dl>`;
}
if (b.term) return `<p>${lead(b.term, b.text)}</p>`;
if (b.list) {
return `<ul>${b.list.map(item => `<li>${Array.isArray(item) ? lead(item[0], item[1]) : inline(item)}</li>`).join('')}</ul>`;
}
return '';
}
function sectionHtml(s) {
const naar = s.place
? placeLink(s.place, t('guide.goTo', { plek: placeNameUnique(s.place) }), 'btn btn-ghost btn-sm guide-goto', CHEVRON_RIGHT)
: '';
const related = s.related.filter(shown);
const zie = related.length
? `<div class="guide-related"><span class="t-label">${esc(t('guide.related'))}</span> ${related
.map(id => `<a href="${esc(hashFor(id))}" data-section="${esc(id)}">${esc(titleOf(id))}</a>`).join(', ')}</div>`
: '';
return `
<article class="guide-section">
<h2 class="t-h3">${esc(titleOf(s.id))}</h2>
<div class="t-body guide-body">${texts().sections[s.id].body.map(block).join('')}</div>
${naar || zie ? `<footer class="guide-foot">${zie}${naar}</footer>` : ''}
</article>`;
}
export async function render(el) {
const groups = GUIDE_GROUPS.map(g => ({ ...g, sections: g.sections.filter(shown) })).filter(g => g.sections.length);
let current = parseHash();
if (!IDS.includes(current) || !shown(current)) {
current = groups[0].sections[0];
history.replaceState(null, '', hashFor(current));
}
el.innerHTML = `
<div class="page-layout">
<aside class="page-aside guide-aside">
<div class="guide-groups" data-group-bar></div>
<nav class="tabset guide-sections" role="tablist" data-sections></nav>
</aside>
<div class="page-body">
<div class="page-header"><h1 class="page-title">${esc(title())}</h1></div>
<div data-groups></div>
</div>
</div>`;
const menu = el.querySelector('[data-sections]');
let panel = null;
let ready = false;
const show = (sid) => {
current = sid;
panel.innerHTML = sectionHtml(BY_ID.get(sid));
menu.querySelectorAll('[data-section]').forEach(b => {
const on = b.dataset.section === sid;
b.classList.toggle('is-on', on);
b.setAttribute('aria-selected', String(on));
});
if (ready && location.hash !== hashFor(sid)) history.pushState(null, '', hashFor(sid));
};
const tabs = createTabs(el.querySelector('[data-groups]'), {
barHost: el.querySelector('[data-group-bar]'),
active: GROUP_OF.get(current),
items: groups.map(g => ({ id: g.id, label: esc(texts().groups[g.id]) })),
render: (gid, host) => {
panel = host;
const groep = groups.find(g => g.id === gid);
menu.innerHTML = groep.sections.map(sid => `
<button type="button" class="tabset-tab" role="tab" data-section="${esc(sid)}">${esc(titleOf(sid))}</button>`).join('');
show(GROUP_OF.get(current) === gid ? current : groep.sections[0]);
},
});
ready = true;
el.querySelector('.page-layout').onclick = e => {
const doel = e.target.closest('[data-section]');
if (!doel || !BY_ID.has(doel.dataset.section)) return;
e.preventDefault();
const sid = doel.dataset.section;
if (tabs && GROUP_OF.get(sid) !== tabs.active) { current = sid; tabs.select(GROUP_OF.get(sid)); }
else show(sid);
};
}