import { useState, useEffect, useMemo } from 'react' import GeschossManager from './components/GeschossManager' import { applyAll, setActiveZeichnungsebene, onMessage, notifyReady, applyVisibility, } from './lib/rhinoBridge' export function recalcOkff(list) { let acc = 0 return list.map(z => { if (z.isGeschoss) { const next = { ...z, okff: parseFloat(acc.toFixed(3)) } acc += (z.hoehe ?? 3.0) return next } return { ...z, okff: undefined } }) } const INITIAL_ZEICHNUNGSEBENEN = recalcOkff([ { id: 'eg', name: 'EG', isGeschoss: true, hoehe: 3.50, schnitthoehe: 1.00, visible: true }, { id: '1og', name: '1OG', isGeschoss: true, hoehe: 3.00, schnitthoehe: 1.00, visible: true }, { id: '2og', name: '2OG', isGeschoss: true, hoehe: 3.00, schnitthoehe: 1.00, visible: true }, ]) export default function ZeichnungsebenenApp() { const [zeichnungsebenen, setZeichnungsebenen] = useState(INITIAL_ZEICHNUNGSEBENEN) const [activeId, setActiveId] = useState('eg') const [appliedZ, setAppliedZ] = useState(INITIAL_ZEICHNUNGSEBENEN) const [zMode, setZMode] = useState('active') useEffect(() => { onMessage('STATE_SYNC', ({ zeichnungsebenen: z }) => { if (z) { const r = recalcOkff(z); setZeichnungsebenen(r); setAppliedZ(r) const active = r.find(zz => zz.id === activeId) || r[0] if (active && !r.find(zz => zz.id === activeId)) { setActiveId(active.id) setActiveZeichnungsebene(active) } } }) onMessage('FIRST_RUN', () => { // Backend hat keine doc.Strings → wir senden den Default an Backend // (nur unsere Slice; ebenen fuellt Backend aus doc.Strings oder leerer // Liste vom anderen Panel beim FIRST_RUN dort). applyAll(INITIAL_ZEICHNUNGSEBENEN, []) setAppliedZ(INITIAL_ZEICHNUNGSEBENEN) const active = INITIAL_ZEICHNUNGSEBENEN.find(zz => zz.id === activeId) || INITIAL_ZEICHNUNGSEBENEN[0] if (active) setActiveZeichnungsebene(active) }) notifyReady() const blockContext = (ev) => ev.preventDefault() document.addEventListener('contextmenu', blockContext) return () => document.removeEventListener('contextmenu', blockContext) // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // Sichtbarkeit live anwenden bei Mode-/Visibility-/Lock-Aenderungen const visibilityKey = useMemo(() => ( activeId + '|' + zMode + '|' + zeichnungsebenen.map(z => `${z.id}:${z.visible !== false ? 1 : 0}:${z.locked ? 1 : 0}`).join(',') ), [activeId, zMode, zeichnungsebenen]) useEffect(() => { const activeZ = zeichnungsebenen.find(z => z.id === activeId) if (activeZ) { // Backend merged mit doc.Strings: aktiveCode + eMode kommen von dort applyVisibility(activeZ, zeichnungsebenen, null, [], zMode, null) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [visibilityKey]) // Auto-Apply bei strukturellen Aenderungen (add/remove/rename, hoehe, // schnitthoehe, hasClipping). Nur ZEICHNUNGSEBENEN-Slice senden — Backend // mergt mit doc.Strings. const zSig = (z) => [ z.id, z.name, z.isGeschoss ? 1 : 0, z.hoehe ?? '', z.schnitthoehe ?? '', z.hasClipping ? 1 : 0, ].join(':') const structureKey = useMemo(() => ( zeichnungsebenen.map(zSig).join(',') ), [zeichnungsebenen]) const appliedStructureKey = useMemo(() => ( appliedZ.map(zSig).join(',') ), [appliedZ]) useEffect(() => { if (structureKey === appliedStructureKey) return console.log('[ZEICHNUNGSEBENEN-UI] structureKey diff → schedule applyAll in 200ms', { zCount: zeichnungsebenen.length, appliedCount: appliedZ.length, }) const t = setTimeout(() => { console.log('[ZEICHNUNGSEBENEN-UI] applyAll firing now', { zCount: zeichnungsebenen.length }) applyAll(zeichnungsebenen, []) setAppliedZ(zeichnungsebenen) }, 200) return () => clearTimeout(t) // eslint-disable-next-line react-hooks/exhaustive-deps }, [structureKey, appliedStructureKey]) const handleActiveChange = (id) => { setActiveId(id) const z = zeichnungsebenen.find(x => x.id === id) if (z) setActiveZeichnungsebene(z) } return (