v0.4.1: UI opgeschoond — tabbladen, iconen, uniforme labels, T-cyclus
- core/ui-kit.js: gedeelde velden/schakelaars/tabs/specificatiekaarten - Bootstrap Icons lokaal (css/vendor, font ingebed) - Woning- en unit-instellingen in tabbladen, labels gestandaardiseerd - Specificaties onder 3D als kaarten - T: standaard -> 3D groot -> alleen render -> standaard Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c690e72d13
commit
3591fad220
+209
-274
@@ -23,11 +23,11 @@
|
||||
const HOUSE_TEMPLATE = `
|
||||
<div id="house-app">
|
||||
<aside id="house-nav">
|
||||
<div class="hn-title">Woning</div>
|
||||
<div class="hn-title"><i class="bi bi-diagram-3"></i> Woning</div>
|
||||
<div id="house-nav-tree"></div>
|
||||
<div class="hn-actions">
|
||||
<button type="button" id="hn-add-unit" class="io-btn">+ Unit</button>
|
||||
<button type="button" id="hn-add-iw" class="io-btn">+ Binnenwand</button>
|
||||
<button type="button" id="hn-add-unit" class="ui-btn"><i class="bi bi-plus-lg"></i><span>Unit</span></button>
|
||||
<button type="button" id="hn-add-iw" class="ui-btn"><i class="bi bi-plus-lg"></i><span>Binnenwand</span></button>
|
||||
</div>
|
||||
<div class="hn-soon">
|
||||
<div class="hn-item disabled" title="Nog niet beschikbaar">Schuine daken</div>
|
||||
@@ -47,14 +47,14 @@ const HOUSE_TEMPLATE = `
|
||||
</div>
|
||||
<div class="controls-grid">
|
||||
<div>
|
||||
<div class="control-group"><label for="h-proj-naam">Projectnaam:</label><div><input type="text" id="h-proj-naam"></div></div>
|
||||
<div class="control-group"><label for="h-proj-plaats">Plaats:</label><div><input type="text" id="h-proj-plaats"></div></div>
|
||||
<div class="control-group"><label for="h-proj-straat">Straat:</label><div><input type="text" id="h-proj-straat"></div></div>
|
||||
<div class="control-group"><label for="h-proj-naam">Projectnaam</label><div><input type="text" id="h-proj-naam"></div></div>
|
||||
<div class="control-group"><label for="h-proj-plaats">Plaats</label><div><input type="text" id="h-proj-plaats"></div></div>
|
||||
<div class="control-group"><label for="h-proj-straat">Straat</label><div><input type="text" id="h-proj-straat"></div></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="control-group"><label for="h-proj-nummer">Projectnummer:</label><div><input type="text" id="h-proj-nummer"></div></div>
|
||||
<div class="control-group"><label for="h-proj-tekenaar">Tekenaar:</label><div><input type="text" id="h-proj-tekenaar"></div></div>
|
||||
<div class="control-group"><label for="h-proj-opmerking">Opmerking:</label><div><input type="text" id="h-proj-opmerking"></div></div>
|
||||
<div class="control-group"><label for="h-proj-nummer">Projectnummer</label><div><input type="text" id="h-proj-nummer"></div></div>
|
||||
<div class="control-group"><label for="h-proj-tekenaar">Tekenaar</label><div><input type="text" id="h-proj-tekenaar"></div></div>
|
||||
<div class="control-group"><label for="h-proj-opmerking">Opmerking</label><div><input type="text" id="h-proj-opmerking"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="material-info">Geldt voor elk blad van de woning-PDF. Titel en elementnummer worden per blad automatisch ingevuld.</div>
|
||||
@@ -70,10 +70,10 @@ const HOUSE_OVERVIEW_TEMPLATE = `
|
||||
<div id="house-overview">
|
||||
<div class="ho-col ho-left">
|
||||
<div id="ho-pane-plan" class="ho-pane">
|
||||
<div class="pane-label">2D PLATTEGROND</div>
|
||||
<div class="pane-label">PLATTEGROND</div>
|
||||
<div class="toolbar-3d">
|
||||
<button type="button" id="ho-tool-select" class="btn-3d-toggle active">Selecteren</button>
|
||||
<button type="button" id="ho-tool-iw" class="btn-3d-toggle">Binnenwand tekenen</button>
|
||||
<button type="button" id="ho-tool-select" class="btn-3d-toggle active" title="Selecteren"><i class="bi bi-cursor"></i>Selecteren</button>
|
||||
<button type="button" id="ho-tool-iw" class="btn-3d-toggle" title="Binnenwand tekenen"><i class="bi bi-pencil"></i>Binnenwand</button>
|
||||
</div>
|
||||
<canvas id="ho-canvas"></canvas>
|
||||
<div id="ho-hint" class="ho-hint"></div>
|
||||
@@ -82,9 +82,9 @@ const HOUSE_OVERVIEW_TEMPLATE = `
|
||||
</div>
|
||||
<div class="ho-col ho-right">
|
||||
<div id="ho-pane-3d" class="ho-pane">
|
||||
<div class="pane-label">3D PERSPECTIEF</div>
|
||||
<div class="pane-label">3D</div>
|
||||
<div class="toolbar-3d" id="ho-toolbar-3d">
|
||||
<button type="button" id="ho-btn-render" class="btn-3d-toggle" title="Avondscène: elke lamp en spot is een echte lichtbron">Volle render</button>
|
||||
<button type="button" id="ho-btn-render" class="btn-3d-toggle" title="Avondscène: elke lamp en spot is een echte lichtbron"><i class="bi bi-moon-stars"></i>Render</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ho-specs"></div>
|
||||
@@ -187,25 +187,25 @@ function mountHouseEditor(container, callbacks) {
|
||||
// ------------------------------------------------------------
|
||||
function renderNav() {
|
||||
const parts = HouseModel.computeParts(state, { skip3D: true });
|
||||
const item = (part, label, meta, cls) =>
|
||||
const item = (part, label, meta, cls, iconName) =>
|
||||
`<div class="hn-item ${cls || ''} ${activePart === part ? 'active' : ''}" data-part="${part}">
|
||||
<span>${label}</span>${meta ? `<span class="hn-meta">${meta}</span>` : ''}</div>`;
|
||||
let html = item('overzicht', 'Overzicht', '', 'hn-top');
|
||||
<span class="hn-label">${iconName ? UI.icon(iconName) : ''}${label}</span>${meta ? `<span class="hn-meta">${meta}</span>` : ''}</div>`;
|
||||
let html = item('overzicht', 'Overzicht', '', 'hn-top', 'grid-1x2');
|
||||
parts.units.forEach(u => {
|
||||
const sel = selection && selection.type === 'unit' && selection.id === u.id && activePart === 'overzicht';
|
||||
html += `<div class="hn-group">`;
|
||||
html += `<div class="hn-item hn-unit ${sel ? 'active' : ''}" data-select-unit="${u.id}"><span>Unit ${u.id}</span><span class="hn-meta">${u.rect.W} × ${u.rect.D}</span></div>`;
|
||||
html += item(`floor:${u.id}`, 'Vloer', u.floorElement, 'hn-sub');
|
||||
html += item(`roof:${u.id}`, 'Dak (plat)', u.roof.element, 'hn-sub');
|
||||
html += `<div class="hn-item hn-unit ${sel ? 'active' : ''}" data-select-unit="${u.id}"><span class="hn-label">${UI.icon('box')}Unit ${u.id}</span><span class="hn-meta">${u.rect.W} × ${u.rect.D}</span></div>`;
|
||||
html += item(`floor:${u.id}`, 'Vloer', u.floorElement, 'hn-sub', 'grid-3x3');
|
||||
html += item(`roof:${u.id}`, 'Dak', u.roof.element, 'hn-sub', 'house');
|
||||
u.walls.forEach(w => {
|
||||
const meta = w.removed ? 'vervalt (naad)' : w.cutsU.length ? `deels naad · ${w.pieces.map(p => p.element).join(', ')}` : w.pieces[0].element;
|
||||
html += item(`wall:${u.id}:${w.side}`, w.label, meta, 'hn-sub' + (w.removed ? ' hn-removed' : ''));
|
||||
const meta = w.removed ? 'vervalt' : w.pieces.map(p => p.element).join(', ');
|
||||
html += item(`wall:${u.id}:${w.side}`, w.label, meta, 'hn-sub' + (w.removed ? ' hn-removed' : ''), 'bricks');
|
||||
});
|
||||
html += `</div>`;
|
||||
});
|
||||
if (parts.interior.length) {
|
||||
html += `<div class="hn-group"><div class="hn-item hn-unit hn-static"><span>Binnenwanden</span></div>`;
|
||||
parts.interior.forEach(p => { html += item(`iw:${p.id}`, p.element, `${p.place.width} mm`, 'hn-sub'); });
|
||||
html += `<div class="hn-group"><div class="hn-item hn-unit hn-static"><span class="hn-label">${UI.icon('distribute-horizontal')}Binnenwanden</span></div>`;
|
||||
parts.interior.forEach(p => { html += item(`iw:${p.id}`, p.element, `${p.place.width} mm`, 'hn-sub', 'distribute-horizontal'); });
|
||||
html += `</div>`;
|
||||
}
|
||||
document.getElementById('house-nav-tree').innerHTML = html;
|
||||
@@ -617,227 +617,172 @@ function mountHouseEditor(container, callbacks) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Eigenschappenpaneel (contextafhankelijk) ---
|
||||
const num = (id, label, value, step) =>
|
||||
`<div class="control-group"><label for="${id}">${label}:</label><div><input type="number" id="${id}" value="${value}" step="${step || 50}"><span class="unit">mm</span></div></div>`;
|
||||
const sel = (id, label, options, value) =>
|
||||
`<div class="control-group"><label for="${id}">${label}:</label><div><select id="${id}">${Object.entries(options).map(([k, v]) => `<option value="${k}" ${k === value ? 'selected' : ''}>${v}</option>`).join('')}</select></div></div>`;
|
||||
// --- Eigenschappenpaneel (contextafhankelijk, opgebouwd met UI-kit) ---
|
||||
const { num, sel, toggle, section, info, button, actions } = UI;
|
||||
const cols = UI.grid; // (grid = de 3D-rasterhulp)
|
||||
UI.wireTabs(propsEl);
|
||||
const head = (iconName, title, sub) =>
|
||||
`<div class="ui-panel-head"><h1>${UI.icon(iconName)}${title}</h1>${sub ? `<span class="ui-sub">${sub}</span>` : ''}</div>`;
|
||||
const after = () => refresh({ keepProps: true }); // getal: panelen blijven staan
|
||||
const rerender = () => refresh(); // keuze: velden kunnen wijzigen
|
||||
|
||||
function renderProps() {
|
||||
const warn = parts.overlaps.length
|
||||
? `<div class="global-warning">Let op: units overlappen elkaar (${parts.overlaps.map(p => p.join(' + ')).join(', ')}).</div>` : '';
|
||||
const errs = parts.rects.filter(r => r.error).map(r => `<div class="global-warning">Unit ${r.id}: ${r.error}</div>`).join('');
|
||||
const warn = (parts.overlaps.length ? UI.warn(`Units overlappen elkaar: ${parts.overlaps.map(p => p.join(' + ')).join(', ')}.`) : '') +
|
||||
parts.rects.filter(r => r.error).map(r => UI.warn(`Unit ${r.id}: ${r.error}`)).join('');
|
||||
|
||||
if (selection && selection.type === 'unit' && unitById(selection.id)) {
|
||||
const u = unitById(selection.id);
|
||||
const isRoot = state.units[0].id === u.id;
|
||||
const a = u.attach;
|
||||
const axis = a && (a.side === 'voor' || a.side === 'achter') ? 'x' : 'z';
|
||||
propsEl.innerHTML = `${warn}${errs}
|
||||
<div class="controls-grid">
|
||||
<div>
|
||||
<h1>Unit ${u.id}</h1>
|
||||
${num('hp-width', 'Breedte (west-oost)', u.unit.floor.floor.width)}
|
||||
${num('hp-depth', 'Diepte (noord-zuid)', u.unit.floor.floor.depth)}
|
||||
${sel('hp-corners', 'Hoeken', UnitModel.CORNER_MODES, u.unit.layout.corners)}
|
||||
</div>
|
||||
<div>
|
||||
<h1>Koppeling</h1>
|
||||
${isRoot ? '<div class="material-info">Basis-unit: noordwest-hoek op (0, 0). Andere units koppelen hieraan.</div>' : `
|
||||
${sel('hp-att-to', 'Gekoppeld aan', Object.fromEntries(state.units.filter(o => o.id !== u.id).map(o => [o.id, 'Unit ' + o.id])), a.to)}
|
||||
${sel('hp-att-side', 'Aan zijde', HouseModel.SIDE_LABELS, a.side)}
|
||||
${sel('hp-att-align', 'Uitlijning', HouseModel.ALIGN_LABELS[axis], a.align)}
|
||||
${num('hp-att-offset', 'Verschuiving', a.offset || 0)}`}
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="controls-grid">
|
||||
<div>
|
||||
<h1>Dak (plat)</h1>
|
||||
${sel('hp-roof-follow', 'Dak volgt vloer', { ja: 'Ja (standaard)', nee: 'Nee, eigen maat' }, u.roof.followFloor ? 'ja' : 'nee')}
|
||||
${u.roof.followFloor ? '' : `
|
||||
${num('hp-roof-w', 'Dakbreedte (west-oost)', u.roof.deck.floor.width)}
|
||||
${num('hp-roof-d', 'Dakdiepte (noord-zuid)', u.roof.deck.floor.depth)}`}
|
||||
</div>
|
||||
<div>
|
||||
${u.roof.followFloor ? '<div class="material-info">Het dak heeft dezelfde maat als de vloer. Kies "Nee, eigen maat" om het dak langer/breder te maken of te verschuiven.</div>' : `
|
||||
<h1>Positie dak</h1>
|
||||
${num('hp-roof-ox', 'Verschuiving oost (+)', u.roof.offsetX || 0)}
|
||||
${num('hp-roof-oz', 'Verschuiving zuid (+)', u.roof.offsetZ || 0)}`}
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="controls-grid">
|
||||
<div>
|
||||
<h1>Dakrand</h1>
|
||||
${sel('hp-fascia', 'Boeidelen', { aan: 'Aan', uit: 'Uit (daktrim 80 mm)' }, u.roof.fascia !== false ? 'aan' : 'uit')}
|
||||
</div>
|
||||
<div>
|
||||
<div class="material-info">Buitenverlichting (type, richting, afstanden) is woning-breed: Overzicht, niets geselecteerd.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ho-prop-actions">
|
||||
<button type="button" class="io-btn" data-go="floor:${u.id}">Vloer bewerken</button>
|
||||
<button type="button" class="io-btn" data-go="roof:${u.id}">Dak bewerken</button>
|
||||
${isRoot ? '' : `<button type="button" class="io-btn io-btn-danger" data-remove-unit="${u.id}">Unit verwijderen</button>`}
|
||||
</div>`;
|
||||
const onNum = (id, fn) => document.getElementById(id) && document.getElementById(id).addEventListener('input', (e) => {
|
||||
const v = parseFloat(e.target.value);
|
||||
if (isFinite(v)) { fn(v); refresh({ keepProps: true }); }
|
||||
});
|
||||
onNum('hp-width', v => { if (v >= 1000) u.unit.floor.floor.width = v; });
|
||||
onNum('hp-depth', v => { if (v >= 1000) u.unit.floor.floor.depth = v; });
|
||||
onNum('hp-att-offset', v => { u.attach.offset = v; });
|
||||
const onSel = (id, fn) => document.getElementById(id) && document.getElementById(id).addEventListener('input', (e) => { fn(e.target.value); refresh(); });
|
||||
onSel('hp-corners', v => { u.unit.layout.corners = v; });
|
||||
onSel('hp-att-to', v => { u.attach.to = v; });
|
||||
onSel('hp-att-side', v => { u.attach.side = v; });
|
||||
onSel('hp-att-align', v => { u.attach.align = v; });
|
||||
onSel('hp-roof-follow', v => { u.roof.followFloor = v === 'ja'; });
|
||||
onSel('hp-fascia', v => { u.roof.fascia = v === 'aan'; });
|
||||
onNum('hp-roof-w', v => { if (v >= 500) u.roof.deck.floor.width = v; });
|
||||
onNum('hp-roof-d', v => { if (v >= 500) u.roof.deck.floor.depth = v; });
|
||||
onNum('hp-roof-ox', v => { u.roof.offsetX = v; });
|
||||
onNum('hp-roof-oz', v => { u.roof.offsetZ = v; });
|
||||
} else if (selection && selection.type === 'wall') {
|
||||
const pu = parts.units.find(x => x.id === selection.unit);
|
||||
const wl = pu && pu.walls.find(x => x.side === selection.side);
|
||||
const piece = wl && wl.pieces.find(p => p.element === selection.element);
|
||||
if (!piece) { selection = null; return renderProps(); }
|
||||
propsEl.innerHTML = `${warn}
|
||||
<h1>${piece.element} — ${piece.label}</h1>
|
||||
<div class="result-row"><span>Lengte volle zijde:</span><span class="highlight">${wl.width} mm</span></div>
|
||||
<div class="result-row"><span>Dit wandstuk:</span><span class="highlight">${piece.place.width} mm (${Math.round(piece.u0)}–${Math.round(piece.u1)} vanaf links, van buitenaf gezien)</span></div>
|
||||
<div class="result-row"><span>Naad met andere unit:</span><span class="highlight">${wl.cutsU.length ? wl.cutsU.map(([a, b]) => `${Math.round(a)}–${Math.round(b)}`).join(', ') + ' mm vervalt' : 'nee'}</span></div>
|
||||
<div class="result-row"><span>Openingen in dit stuk:</span><span class="highlight">${piece.config.openings.length}</span></div>
|
||||
${state.settings.lighting.mode === 'gevel' ? `
|
||||
<div class="divider"></div>
|
||||
<h1>Gevellampen op deze wand</h1>
|
||||
${sel('hw-light', 'Lampen', LightingModel.WALL_OVERRIDES, unitById(pu.id).lighting.walls[wl.side] || 'auto')}
|
||||
<div class="result-row"><span>Lampen op dit wandstuk:</span><span class="highlight">${piece.lamps.length}</span></div>
|
||||
<div class="material-info">Automatisch: vanaf ${state.settings.lighting.endOffset} mm van hoek/uiteinde, alleen op vrije stukken gevel ≥ ${state.settings.lighting.minWallWidth} mm op ${state.settings.lighting.lampHeight} mm hoogte (smallere stukken krijgen geen lampen). Override: ${state.settings.lighting.openingOffset} mm naast elke opening.</div>`
|
||||
: '<div class="material-info">Gevellampen: kies in de woning-instellingen "Buitenverlichting: Gevellampen".</div>'}
|
||||
${wl.dropped.length ? `<div class="global-warning">${wl.dropped.length} opening(en) van deze zijde vallen in een naad en vervallen.</div>` : ''}
|
||||
<div class="ho-prop-actions"><button type="button" class="io-btn" data-go="wall:${pu.id}:${wl.side}">Wand bewerken</button></div>`;
|
||||
const hw = document.getElementById('hw-light');
|
||||
if (hw) hw.addEventListener('input', (e) => { unitById(pu.id).lighting.walls[wl.side] = e.target.value; refresh(); });
|
||||
} else if (selection && selection.type === 'iw' && iwById(selection.id)) {
|
||||
const iw = iwById(selection.id);
|
||||
const p = parts.interior.find(x => x.id === iw.id);
|
||||
propsEl.innerHTML = `${warn}
|
||||
<div class="controls-grid">
|
||||
<div>
|
||||
<h1>${p.element} — binnenwand</h1>
|
||||
${num('hi-x1', 'Begin X', iw.x1)}${num('hi-z1', 'Begin Y', iw.z1)}
|
||||
${num('hi-x2', 'Eind X', iw.x2)}${num('hi-z2', 'Eind Y', iw.z2)}
|
||||
</div>
|
||||
<div>
|
||||
<h1>Opbouw</h1>
|
||||
${num('hi-thick', 'Dikte', HouseModel.thicknessOf(iw.wall), 5)}
|
||||
${num('hi-height', 'Hoogte', iw.wall.wall.heightAll)}
|
||||
<div class="result-row"><span>Lengte:</span><span class="highlight">${p.place.width} mm</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ho-prop-actions">
|
||||
<button type="button" class="io-btn" data-go="iw:${iw.id}">Wand bewerken (openingen)</button>
|
||||
<button type="button" class="io-btn io-btn-danger" data-remove-iw="${iw.id}">Verwijderen</button>
|
||||
</div>`;
|
||||
const onNum = (id, fn) => document.getElementById(id).addEventListener('input', (e) => {
|
||||
const v = parseFloat(e.target.value);
|
||||
if (isFinite(v)) { fn(v); refresh({ keepProps: true }); }
|
||||
});
|
||||
['x1', 'z1', 'x2', 'z2'].forEach(f => onNum('hi-' + f, v => { iw[f] = v; }));
|
||||
onNum('hi-thick', v => { if (v >= 40) HouseModel.setThickness(iw.wall, v); });
|
||||
onNum('hi-height', v => { if (v >= 500) iw.wall.wall.heightAll = iw.wall.wall.heightLeft = iw.wall.wall.heightRight = v; });
|
||||
} else {
|
||||
propsEl.innerHTML = `${warn}${errs}
|
||||
<h1>Woning</h1>
|
||||
<div class="material-info">${state.units.length} unit(s), ${state.interiorWalls.length} binnenwand(en).
|
||||
Klik in de plattegrond op een unit, wand of binnenwand om die te selecteren; dubbelklik om te bewerken.
|
||||
Waar units tegen elkaar staan (oranje gestippeld) vervalt de wand.</div>
|
||||
<div class="ho-prop-actions">
|
||||
<button type="button" class="io-btn" data-action="add-unit">+ Unit toevoegen</button>
|
||||
<button type="button" class="io-btn" data-action="draw-iw">Binnenwand tekenen</button>
|
||||
<button type="button" class="io-btn" data-action="project">Project eigenschappen</button>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
${renderSettingsHtml()}`;
|
||||
wireSettings();
|
||||
}
|
||||
if (selection && selection.type === 'unit' && unitById(selection.id)) renderUnitProps(unitById(selection.id), warn);
|
||||
else if (selection && selection.type === 'wall') renderWallProps(warn);
|
||||
else if (selection && selection.type === 'iw' && iwById(selection.id)) renderIwProps(iwById(selection.id), warn);
|
||||
else renderHouseProps(warn);
|
||||
}
|
||||
|
||||
// Woning-instellingen: h.o.h. voor ALLE buiten-/binnenwanden + dakrand.
|
||||
function renderSettingsHtml() {
|
||||
const sp = state.settings.spacing, r = state.settings.roof, lt = state.settings.lighting;
|
||||
return `
|
||||
<div class="controls-grid">
|
||||
<div>
|
||||
<h1>Gevelbekleding (test, alle buitenwanden)</h1>
|
||||
${sel('hs-cladding', 'Richting WEO/Novalit', WallModel.CLADDING_DIRECTIONS, state.settings.cladding)}
|
||||
${sel('hs-trasraam', 'Trasraam (cement-gebonden plaat)', { aan: 'Aan', uit: 'Uit' }, state.settings.trasraam.enabled ? 'aan' : 'uit')}
|
||||
${state.settings.trasraam.enabled ? num('hs-trasraam-h', 'Hoogte trasraam (vanaf bovenkant vloer)', state.settings.trasraam.height, 10) : ''}
|
||||
${sel('hs-sandwich', `Sandwichpanelen rondom (${SandwichModel.DEPTH_MM} mm de grond in)`, { aan: 'Aan', uit: 'Uit' }, state.settings.sandwich ? 'aan' : 'uit')}
|
||||
<div class="divider"></div>
|
||||
<h1>Regelwerk h.o.h. (alle wanden)</h1>
|
||||
${num('hs-ext-min', 'Buitenwanden min.', sp.exterior.min, 10)}
|
||||
${num('hs-ext-max', 'Buitenwanden max.', sp.exterior.max, 10)}
|
||||
${num('hs-int-min', 'Binnenwanden min.', sp.interior.min, 10)}
|
||||
${num('hs-int-max', 'Binnenwanden max.', sp.interior.max, 10)}
|
||||
</div>
|
||||
<div>
|
||||
<h1>Dakrand (maten, hele woning)</h1>
|
||||
${num('hs-overhang', `Overstek boeideel (${RoofModel.OVERHANG_MIN}–${RoofModel.OVERHANG_MAX})`, r.overhang, 10)}
|
||||
${num('hs-fascia-h', 'Hoogte boeideel', r.fasciaHeight, 10)}
|
||||
${num('hs-trim-h', 'Hoogte daktrim', r.trimHeight, 5)}
|
||||
${num('hs-spot-min', 'Spots min. h.o.h.', r.spotMin, 50)}
|
||||
${num('hs-spot-max', 'Spots max. h.o.h.', r.spotMax, 50)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="controls-grid">
|
||||
<div>
|
||||
<h1>Buitenverlichting (hele woning)</h1>
|
||||
${sel('hs-light-mode', 'Type', LightingModel.MODES, lt.mode)}
|
||||
${lt.mode === 'gevel' ? sel('hs-light-dir', 'Lichtrichting', LightingModel.DIRECTIONS, lt.direction) : ''}
|
||||
${lt.mode === 'spots' ? '<div class="material-info">Boeideel-spots komen alleen bij units met boeidelen.</div>' : ''}
|
||||
${num('hs-lamp-h', 'Lamphoogte', lt.lampHeight, 50)}
|
||||
${num('hs-lamp-min', 'Lampen min. h.o.h.', lt.lampMin, 50)}
|
||||
${num('hs-lamp-max', 'Lampen max. h.o.h.', lt.lampMax, 50)}
|
||||
</div>
|
||||
<div>
|
||||
<h1> </h1>
|
||||
${num('hs-lamp-w', 'Min. vrije gevelbreedte', lt.minWallWidth, 50)}
|
||||
${num('hs-lamp-end', 'Vanaf hoek/uiteinde', lt.endOffset, 50)}
|
||||
${num('hs-lamp-open', 'Override: naast opening', lt.openingOffset, 50)}
|
||||
</div>
|
||||
</div>`;
|
||||
function renderUnitProps(u, warn) {
|
||||
const isRoot = state.units[0].id === u.id;
|
||||
const a = u.attach;
|
||||
const axis = a && (a.side === 'voor' || a.side === 'achter') ? 'x' : 'z';
|
||||
const f = u.unit.floor.floor;
|
||||
const koppeling = isRoot
|
||||
? info('Basis-unit: de noordwesthoek ligt op (0, 0). Andere units koppelen hieraan.')
|
||||
: sel('hp-att-to', 'Gekoppeld aan', Object.fromEntries(state.units.filter(o => o.id !== u.id).map(o => [o.id, 'Unit ' + o.id])), a.to) +
|
||||
sel('hp-att-side', 'Zijde', HouseModel.SIDE_LABELS, a.side) +
|
||||
sel('hp-att-align', 'Uitlijning', HouseModel.ALIGN_LABELS[axis], a.align) +
|
||||
num('hp-att-offset', 'Verschuiving', a.offset || 0);
|
||||
const dak = toggle('hp-roof-follow', 'Maat volgt vloer', u.roof.followFloor) +
|
||||
(u.roof.followFloor ? '' :
|
||||
num('hp-roof-w', 'Breedte', u.roof.deck.floor.width, { hint: 'West–oost' }) +
|
||||
num('hp-roof-d', 'Diepte', u.roof.deck.floor.depth, { hint: 'Noord–zuid' }) +
|
||||
num('hp-roof-ox', 'Verschuiving oost', u.roof.offsetX || 0) +
|
||||
num('hp-roof-oz', 'Verschuiving zuid', u.roof.offsetZ || 0)) +
|
||||
toggle('hp-fascia', 'Boeidelen', u.roof.fascia !== false, { hint: 'Uit = daktrim van 80 mm' });
|
||||
|
||||
propsEl.innerHTML = warn + head('box', `Unit ${u.id}`, `${f.width} × ${f.depth} mm`) +
|
||||
UI.tabs('unit', [
|
||||
{ key: 'maat', label: 'Afmetingen', icon: 'rulers', html: cols(
|
||||
num('hp-width', 'Breedte', f.width, { hint: 'West–oost' }) + num('hp-depth', 'Diepte', f.depth, { hint: 'Noord–zuid' }),
|
||||
sel('hp-corners', 'Hoekaansluiting', UnitModel.CORNER_MODES, u.unit.layout.corners)) },
|
||||
{ key: 'koppeling', label: 'Koppeling', icon: 'link-45deg', html: cols(koppeling, '') },
|
||||
{ key: 'dak', label: 'Dak', icon: 'house', html: cols(dak, u.roof.followFloor ? info('Het dak heeft de maat van de vloer. Zet "Maat volgt vloer" uit voor een eigen maat of verschuiving.') : '') }
|
||||
]) +
|
||||
actions(
|
||||
button('Vloer bewerken', 'grid-3x3', `data-go="floor:${u.id}"`),
|
||||
button('Dak bewerken', 'house', `data-go="roof:${u.id}"`),
|
||||
isRoot ? '' : button('Unit verwijderen', 'trash', `data-remove-unit="${u.id}"`, 'ui-btn-danger'));
|
||||
|
||||
UI.onNum('hp-width', v => { if (v >= 1000) f.width = v; }, after);
|
||||
UI.onNum('hp-depth', v => { if (v >= 1000) f.depth = v; }, after);
|
||||
UI.onNum('hp-att-offset', v => { u.attach.offset = v; }, after);
|
||||
UI.onSel('hp-corners', v => { u.unit.layout.corners = v; }, rerender);
|
||||
UI.onSel('hp-att-to', v => { u.attach.to = v; }, rerender);
|
||||
UI.onSel('hp-att-side', v => { u.attach.side = v; }, rerender);
|
||||
UI.onSel('hp-att-align', v => { u.attach.align = v; }, rerender);
|
||||
UI.onToggle('hp-roof-follow', v => { u.roof.followFloor = v; }, rerender);
|
||||
UI.onToggle('hp-fascia', v => { u.roof.fascia = v; }, rerender);
|
||||
UI.onNum('hp-roof-w', v => { if (v >= 500) u.roof.deck.floor.width = v; }, after);
|
||||
UI.onNum('hp-roof-d', v => { if (v >= 500) u.roof.deck.floor.depth = v; }, after);
|
||||
UI.onNum('hp-roof-ox', v => { u.roof.offsetX = v; }, after);
|
||||
UI.onNum('hp-roof-oz', v => { u.roof.offsetZ = v; }, after);
|
||||
}
|
||||
function wireSettings() {
|
||||
const sp = state.settings.spacing, r = state.settings.roof;
|
||||
const onNum = (id, fn) => { const el = document.getElementById(id); if (el) el.addEventListener('input', (e) => { const v = parseFloat(e.target.value); if (isFinite(v) && v > 0) { fn(v); refresh({ keepProps: true }); } }); };
|
||||
const onSel = (id, fn) => { const el = document.getElementById(id); if (el) el.addEventListener('input', (e) => { fn(e.target.value); refresh(); }); };
|
||||
onSel('hs-cladding', v => { state.settings.cladding = v; });
|
||||
onSel('hs-trasraam', v => { state.settings.trasraam.enabled = v === 'aan'; });
|
||||
onSel('hs-sandwich', v => { state.settings.sandwich = v === 'aan'; });
|
||||
onNum('hs-trasraam-h', v => { state.settings.trasraam.height = v; });
|
||||
onNum('hs-ext-min', v => { sp.exterior.min = v; });
|
||||
onNum('hs-ext-max', v => { sp.exterior.max = v; });
|
||||
onNum('hs-int-min', v => { sp.interior.min = v; });
|
||||
onNum('hs-int-max', v => { sp.interior.max = v; });
|
||||
|
||||
function renderWallProps(warn) {
|
||||
const pu = parts.units.find(x => x.id === selection.unit);
|
||||
const wl = pu && pu.walls.find(x => x.side === selection.side);
|
||||
const piece = wl && wl.pieces.find(p => p.element === selection.element);
|
||||
if (!piece) { selection = null; return renderProps(); }
|
||||
const lt = state.settings.lighting;
|
||||
onNum('hs-overhang', v => { r.overhang = Math.min(RoofModel.OVERHANG_MAX, Math.max(RoofModel.OVERHANG_MIN, v)); });
|
||||
onNum('hs-fascia-h', v => { r.fasciaHeight = v; });
|
||||
onNum('hs-spot-min', v => { r.spotMin = v; });
|
||||
onSel('hs-light-mode', v => { lt.mode = v; });
|
||||
onSel('hs-light-dir', v => { lt.direction = v; });
|
||||
onNum('hs-lamp-h', v => { lt.lampHeight = v; });
|
||||
onNum('hs-lamp-min', v => { lt.lampMin = v; });
|
||||
onNum('hs-lamp-max', v => { lt.lampMax = v; });
|
||||
onNum('hs-lamp-w', v => { lt.minWallWidth = v; });
|
||||
onNum('hs-lamp-end', v => { lt.endOffset = v; });
|
||||
onNum('hs-lamp-open', v => { lt.openingOffset = v; });
|
||||
onNum('hs-spot-max', v => { r.spotMax = v; });
|
||||
onNum('hs-trim-h', v => { r.trimHeight = v; });
|
||||
const rows = [
|
||||
['Lengte zijde', wl.width, 'mm'],
|
||||
['Lengte wandstuk', piece.place.width, 'mm'],
|
||||
['Positie', `${Math.round(piece.u0)} – ${Math.round(piece.u1)}`, 'mm'],
|
||||
['Naad', wl.cutsU.length ? wl.cutsU.map(([a, b]) => `${Math.round(a)} – ${Math.round(b)}`).join(', ') : 'geen', wl.cutsU.length ? 'mm' : ''],
|
||||
['Openingen', piece.config.openings.length, '']
|
||||
];
|
||||
const lampen = lt.mode === 'gevel'
|
||||
? sel('hw-light', 'Lampen', LightingModel.WALL_OVERRIDES, unitById(pu.id).lighting.walls[wl.side] || 'auto',
|
||||
{ hint: `Automatisch: ${lt.endOffset} mm uit de hoek, alleen op vrije gevel van min. ${lt.minWallWidth} mm. Naast openingen: ${lt.openingOffset} mm naast elke opening.` }) +
|
||||
UI.specCard('Op dit wandstuk', 'lightbulb', [['Gevellampen', piece.lamps.length, 'st.']])
|
||||
: info('Gevellampen staan uit. Kies bij Woning › Verlichting het type "Gevellampen".');
|
||||
propsEl.innerHTML = warn + head('bricks', piece.element, piece.label) +
|
||||
cols(UI.specCard('Wandstuk', 'rulers', rows), section('Gevellampen', 'lightbulb', lampen)) +
|
||||
(wl.dropped.length ? UI.warn(`${wl.dropped.length} opening(en) van deze zijde vallen in een naad en vervallen.`) : '') +
|
||||
actions(button('Wand bewerken', 'pencil-square', `data-go="wall:${pu.id}:${wl.side}"`));
|
||||
UI.onSel('hw-light', v => { unitById(pu.id).lighting.walls[wl.side] = v; }, rerender);
|
||||
}
|
||||
|
||||
function renderIwProps(iw, warn) {
|
||||
const p = parts.interior.find(x => x.id === iw.id);
|
||||
propsEl.innerHTML = warn + head('distribute-horizontal', p.element, `Binnenwand · ${p.place.width} mm`) +
|
||||
cols(
|
||||
section('Positie', 'crosshair', num('hi-x1', 'Begin X', iw.x1) + num('hi-z1', 'Begin Y', iw.z1) + num('hi-x2', 'Eind X', iw.x2) + num('hi-z2', 'Eind Y', iw.z2)),
|
||||
section('Opbouw', 'layers', num('hi-thick', 'Dikte', HouseModel.thicknessOf(iw.wall), { step: 5 }) + num('hi-height', 'Hoogte', iw.wall.wall.heightAll))) +
|
||||
actions(
|
||||
button('Openingen bewerken', 'pencil-square', `data-go="iw:${iw.id}"`),
|
||||
button('Verwijderen', 'trash', `data-remove-iw="${iw.id}"`, 'ui-btn-danger'));
|
||||
['x1', 'z1', 'x2', 'z2'].forEach(k => UI.onNum('hi-' + k, v => { iw[k] = v; }, after));
|
||||
UI.onNum('hi-thick', v => { if (v >= 40) HouseModel.setThickness(iw.wall, v); }, after);
|
||||
UI.onNum('hi-height', v => { if (v >= 500) iw.wall.wall.heightAll = iw.wall.wall.heightLeft = iw.wall.wall.heightRight = v; }, after);
|
||||
}
|
||||
|
||||
// Woning-instellingen (niets geselecteerd): alles wat voor de hele woning
|
||||
// geldt, in tabbladen.
|
||||
function renderHouseProps(warn) {
|
||||
const st = state.settings, sp = st.spacing, r = st.roof, lt = st.lighting;
|
||||
const pos = { positive: true };
|
||||
const verlichting = lt.mode === 'gevel'
|
||||
? cols(
|
||||
section('Gevellampen', 'lightbulb', sel('hs-light-dir', 'Lichtrichting', LightingModel.DIRECTIONS, lt.direction) +
|
||||
num('hs-lamp-h', 'Hoogte', lt.lampHeight, { hint: 'Vanaf bovenkant vloer' }) +
|
||||
num('hs-lamp-min', 'Afstand min.', lt.lampMin, { hint: 'Hart-op-hart' }) + num('hs-lamp-max', 'Afstand max.', lt.lampMax, { hint: 'Hart-op-hart' })),
|
||||
section('Plaatsing', 'distribute-horizontal', num('hs-lamp-w', 'Min. gevelbreedte', lt.minWallWidth, { hint: 'Smallere vrije stukken gevel krijgen geen lamp' }) +
|
||||
num('hs-lamp-end', 'Afstand tot hoek', lt.endOffset) +
|
||||
num('hs-lamp-open', 'Afstand tot opening', lt.openingOffset, { hint: 'Voor wanden met de instelling "Naast openingen"' })))
|
||||
: lt.mode === 'spots'
|
||||
? cols(section('Boeideel-spots', 'lightbulb', num('hs-spot-min', 'Afstand min.', r.spotMin, { hint: 'Hart-op-hart' }) + num('hs-spot-max', 'Afstand max.', r.spotMax, { hint: 'Hart-op-hart' })),
|
||||
info('Spots komen alleen onder units met boeidelen.'))
|
||||
: '';
|
||||
|
||||
propsEl.innerHTML = warn + head('house-door', 'Woning', `${state.units.length} unit(s) · ${state.interiorWalls.length} binnenwand(en)`) +
|
||||
UI.tabs('woning', [
|
||||
{ key: 'algemeen', label: 'Algemeen', icon: 'house-door', html:
|
||||
info('Klik in de plattegrond op een unit, wand of binnenwand om te selecteren; dubbelklik om te bewerken. Waar units tegen elkaar staan (oranje gestippeld) vervalt de wand.') +
|
||||
actions(button('Unit toevoegen', 'plus-lg', 'data-action="add-unit"'), button('Binnenwand tekenen', 'pencil', 'data-action="draw-iw"'),
|
||||
button('Projectgegevens', 'card-text', 'data-action="project"')) },
|
||||
{ key: 'gevel', label: 'Gevel', icon: 'bricks', html: cols(
|
||||
section('Gevelbekleding', 'grip-horizontal', sel('hs-cladding', 'Richting', WallModel.CLADDING_DIRECTIONS, st.cladding, { hint: 'WEO/Novalit, voorlopig voor alle buitenwanden' })) +
|
||||
section('Trasraam', 'bricks', toggle('hs-trasraam', 'Trasraam', st.trasraam.enabled, { hint: 'Cement-gebonden plaat onderaan de gevel' }) +
|
||||
(st.trasraam.enabled ? num('hs-trasraam-h', 'Hoogte', st.trasraam.height, { step: 10, hint: 'Vanaf bovenkant vloer' }) : '')),
|
||||
section('Sandwichpanelen', 'bounding-box', toggle('hs-sandwich', 'Rondom woning', st.sandwich,
|
||||
{ hint: `${SandwichModel.PANEL_WIDTH_MM} × ${SandwichModel.PANEL_LENGTH_MM} × ${SandwichModel.THICK_MM} mm, vanaf bovenkant vloer ${SandwichModel.DEPTH_MM} mm de grond in` }))) },
|
||||
{ key: 'constructie', label: 'Regelwerk', icon: 'columns-gap', html: cols(
|
||||
section('Buitenwanden', 'bricks', num('hs-ext-min', 'Afstand min.', sp.exterior.min, { step: 10, hint: 'Hart-op-hart stijlen' }) + num('hs-ext-max', 'Afstand max.', sp.exterior.max, { step: 10, hint: 'Hart-op-hart stijlen' })),
|
||||
section('Binnenwanden', 'distribute-horizontal', num('hs-int-min', 'Afstand min.', sp.interior.min, { step: 10, hint: 'Hart-op-hart stijlen' }) + num('hs-int-max', 'Afstand max.', sp.interior.max, { step: 10, hint: 'Hart-op-hart stijlen' }))) },
|
||||
{ key: 'dak', label: 'Dakrand', icon: 'house', html: cols(
|
||||
section('Boeideel', 'border-outer', num('hs-overhang', 'Overstek', r.overhang, { step: 10, hint: `${RoofModel.OVERHANG_MIN} – ${RoofModel.OVERHANG_MAX} mm` }) + num('hs-fascia-h', 'Hoogte', r.fasciaHeight, { step: 10 })),
|
||||
section('Daktrim', 'border-top', num('hs-trim-h', 'Hoogte', r.trimHeight, { step: 5, hint: 'Bij units zonder boeidelen' }))) },
|
||||
{ key: 'verlichting', label: 'Verlichting', icon: 'lightbulb', html:
|
||||
cols(sel('hs-light-mode', 'Type', LightingModel.MODES, lt.mode), '') + verlichting }
|
||||
]);
|
||||
|
||||
UI.onSel('hs-cladding', v => { st.cladding = v; }, rerender);
|
||||
UI.onToggle('hs-trasraam', v => { st.trasraam.enabled = v; }, rerender);
|
||||
UI.onToggle('hs-sandwich', v => { st.sandwich = v; }, rerender);
|
||||
UI.onNum('hs-trasraam-h', v => { st.trasraam.height = v; }, after, pos);
|
||||
UI.onNum('hs-ext-min', v => { sp.exterior.min = v; }, after, pos);
|
||||
UI.onNum('hs-ext-max', v => { sp.exterior.max = v; }, after, pos);
|
||||
UI.onNum('hs-int-min', v => { sp.interior.min = v; }, after, pos);
|
||||
UI.onNum('hs-int-max', v => { sp.interior.max = v; }, after, pos);
|
||||
UI.onNum('hs-overhang', v => { r.overhang = Math.min(RoofModel.OVERHANG_MAX, Math.max(RoofModel.OVERHANG_MIN, v)); }, after, pos);
|
||||
UI.onNum('hs-fascia-h', v => { r.fasciaHeight = v; }, after, pos);
|
||||
UI.onNum('hs-trim-h', v => { r.trimHeight = v; }, after, pos);
|
||||
UI.onNum('hs-spot-min', v => { r.spotMin = v; }, after, pos);
|
||||
UI.onNum('hs-spot-max', v => { r.spotMax = v; }, after, pos);
|
||||
UI.onSel('hs-light-mode', v => { lt.mode = v; }, rerender);
|
||||
UI.onSel('hs-light-dir', v => { lt.direction = v; }, rerender);
|
||||
UI.onNum('hs-lamp-h', v => { lt.lampHeight = v; }, after, pos);
|
||||
UI.onNum('hs-lamp-min', v => { lt.lampMin = v; }, after, pos);
|
||||
UI.onNum('hs-lamp-max', v => { lt.lampMax = v; }, after, pos);
|
||||
UI.onNum('hs-lamp-w', v => { lt.minWallWidth = v; }, after, pos);
|
||||
UI.onNum('hs-lamp-end', v => { lt.endOffset = v; }, after, pos);
|
||||
UI.onNum('hs-lamp-open', v => { lt.openingOffset = v; }, after, pos);
|
||||
}
|
||||
|
||||
propsEl.addEventListener('click', (e) => {
|
||||
@@ -859,37 +804,29 @@ function mountHouseEditor(container, callbacks) {
|
||||
else if (act.dataset.action === 'project') openProjectProperties();
|
||||
});
|
||||
|
||||
// --- Specificaties/totalen ---
|
||||
// --- Specificaties/totalen (onder de 3D) ---
|
||||
function renderSpecs(totals) {
|
||||
const row = (label, value) => `<div class="result-row"><span>${label}:</span><span class="highlight">${value}</span></div>`;
|
||||
document.getElementById('ho-specs').innerHTML = `
|
||||
<div class="controls-grid">
|
||||
<div>
|
||||
<div class="spec-section"><h1>Gewicht</h1>
|
||||
${totals.weights.map(r => row(r.label, houseFmt(r.kg) + ' kg')).join('')}
|
||||
<div class="divider"></div>${row('Totaal woning', houseFmt(totals.totalKg) + ' kg')}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="spec-section"><h1>Houtwerk & isolatie</h1>
|
||||
${row('Aantal balken', totals.beamCount + ' stuks')}
|
||||
${row('Volume hout', houseFmt(totals.woodM3, 3) + ' m3')}
|
||||
${row('Latwerk 20x30 (kruislat)', totals.battens.count + ' st. à ' + WallModel.BATTEN.length + ' mm')}
|
||||
${row('Inkoop wandbalken', WallModel.purchaseText(totals.purchase))}
|
||||
${row('Snijverlies', houseFmt(totals.purchaseWasteMM / 1000, 2) + ' m (' + houseFmt(totals.purchaseStockMM ? totals.purchaseWasteMM / totals.purchaseStockMM * 100 : 0) + '%)')}
|
||||
${row('Glaswol (isolatie)', houseFmt(totals.isolatieM3, 3) + ' m3')}
|
||||
</div>
|
||||
<div class="spec-section"><h1>Plaatwerk</h1>
|
||||
${totals.plaatwerk.filter(p => p.m2 > 0).map(p => row(p.naam, houseFmt(p.m2, 2) + ' m2')).join('')}
|
||||
</div>
|
||||
${totals.roofEdge ? `<div class="spec-section"><h1>Dakrand & buitenverlichting</h1>
|
||||
${totals.roofEdge.fasciaLengthM > 0 ? row('Boeidelen', houseFmt(totals.roofEdge.fasciaLengthM, 2) + ' m · ' + totals.roofEdge.fasciaHeight + ' mm hoog') + row('Onderplaat boeideel', houseFmt(totals.roofEdge.soffitM2, 2) + ' m2') : ''}
|
||||
${totals.roofEdge.trimLengthM > 0 ? row('Daktrim', houseFmt(totals.roofEdge.trimLengthM, 2) + ' m · ' + totals.roofEdge.trimHeight + ' mm hoog') : ''}
|
||||
${row('Boeideel-spots', totals.roofEdge.spots + ' stuks')}
|
||||
${row('Gevellampen', totals.lamps.total + ' stuks')}
|
||||
</div>` : ''}
|
||||
</div>
|
||||
</div>`;
|
||||
const f = houseFmt;
|
||||
const cards = [
|
||||
UI.specCard('Gewicht', 'speedometer2', totals.weights.map(r => [r.label, f(r.kg), 'kg']), ['Totaal woning', f(totals.totalKg), 'kg']),
|
||||
UI.specCard('Hout & isolatie', 'tree', [
|
||||
['Balken', totals.beamCount, 'st.'],
|
||||
['Volume hout', f(totals.woodM3, 3), 'm³'],
|
||||
['Inkoop wandbalken', WallModel.purchaseText(totals.purchase), ''],
|
||||
['Snijverlies', `${f(totals.purchaseWasteMM / 1000, 2)} m · ${f(totals.purchaseStockMM ? totals.purchaseWasteMM / totals.purchaseStockMM * 100 : 0)}`, '%'],
|
||||
[`Latwerk ${WallModel.BATTEN.thick}×${WallModel.BATTEN.width}`, `${totals.battens.count} × ${WallModel.BATTEN.length}`, 'mm'],
|
||||
['Glaswol', f(totals.isolatieM3, 3), 'm³']
|
||||
]),
|
||||
UI.specCard('Plaatwerk', 'layers', totals.plaatwerk.filter(p => p.m2 > 0).map(p => [p.naam, f(p.m2, 2), 'm²']))
|
||||
];
|
||||
if (totals.roofEdge) {
|
||||
const e = totals.roofEdge, rows = [];
|
||||
if (e.fasciaLengthM > 0) rows.push([`Boeidelen · ${e.fasciaHeight} mm`, f(e.fasciaLengthM, 2), 'm'], ['Onderplaat boeideel', f(e.soffitM2, 2), 'm²']);
|
||||
if (e.trimLengthM > 0) rows.push([`Daktrim · ${e.trimHeight} mm`, f(e.trimLengthM, 2), 'm']);
|
||||
rows.push(['Boeideel-spots', e.spots, 'st.'], ['Gevellampen', totals.lamps.total, 'st.']);
|
||||
cards.push(UI.specCard('Dakrand & verlichting', 'lightbulb', rows));
|
||||
}
|
||||
document.getElementById('ho-specs').innerHTML = `<div class="ui-spec-grid">${cards.join('')}</div>`;
|
||||
}
|
||||
|
||||
// --- Alles opnieuw doorrekenen ---
|
||||
@@ -952,10 +889,8 @@ function mountHouseEditor(container, callbacks) {
|
||||
}
|
||||
window.addEventListener('resize', onResize);
|
||||
|
||||
function toggleFullscreen(cls, other) {
|
||||
const el = document.getElementById('house-overview');
|
||||
el.classList.remove(other);
|
||||
el.classList.toggle(cls);
|
||||
function toggleView(fn) {
|
||||
fn(document.getElementById('house-overview'));
|
||||
requestAnimationFrame(onResize);
|
||||
}
|
||||
|
||||
@@ -974,8 +909,8 @@ function mountHouseEditor(container, callbacks) {
|
||||
},
|
||||
refresh,
|
||||
setTool,
|
||||
toggleFullscreen3D: () => toggleFullscreen('fullscreen-3d', 'fullscreen-2d'),
|
||||
toggleFullscreen2D: () => toggleFullscreen('fullscreen-2d', 'fullscreen-3d')
|
||||
toggleFullscreen3D: () => toggleView(UI.cycle3D),
|
||||
toggleFullscreen2D: () => toggleView(UI.toggle2D)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user