c4c9e56b2c
Der Section-Header mit dem Panel-Namen ("Ebenen" / "Zeichnungsebenen")
war redundant — der Panel-Tab in Rhino hat den Namen schon. Stattdessen
direkt mit der Sichtbarkeit-Dropdown beginnen.
EbenenManager:
- `<Section>`-Wrapper entfernt, Section-Import raus.
- "+"-Button neben der Sichtbarkeit-Dropdown (flex: 1).
GeschossManager:
- Gleich. "+"- und Edit-Buttons (Bleistift) neben der Sichtbarkeit-
Dropdown.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
186 lines
6.1 KiB
React
186 lines
6.1 KiB
React
import { useState } from 'react'
|
|
import Icon from './Icon'
|
|
import GeschossDialog from './GeschossDialog'
|
|
import GeschossSettingsDialog from './GeschossSettingsDialog'
|
|
|
|
function GeschossBadge({ name }) {
|
|
return <span className="chip chip-info">{name}</span>
|
|
}
|
|
|
|
function ZeichnungsebeneRow({ z, active, mode, onClick, onToggleVisible, onSettings }) {
|
|
// Eye-State auch fuer die aktive Zeichnungsebene anzeigen (User-Intention)
|
|
const eyeShown = mode !== 'active'
|
|
const isGeschoss = !!z.isGeschoss
|
|
return (
|
|
<div
|
|
onClick={onClick}
|
|
style={{
|
|
display: 'flex', alignItems: 'center', gap: 8,
|
|
padding: '4px 12px',
|
|
margin: active ? '1px 6px' : '0',
|
|
background: active ? 'var(--active-dim)' : 'var(--bg-item)',
|
|
// Pill-Form fuer die aktive Zeichnungsebene
|
|
borderRadius: active ? 999 : 0,
|
|
borderLeft: active ? 'none' : '3px solid transparent',
|
|
borderBottom: active ? 'none' : '1px solid var(--border-light)',
|
|
boxShadow: active ? 'inset 0 0 0 1px var(--active-light)' : 'none',
|
|
cursor: 'pointer',
|
|
userSelect: 'none',
|
|
opacity: (!active && z.visible === false && mode !== 'all') ? 0.45 : 1,
|
|
}}
|
|
>
|
|
<span style={{
|
|
fontWeight: active ? 700 : 500,
|
|
fontSize: 12,
|
|
color: active ? 'var(--active-light)' : 'var(--text-label)',
|
|
flex: 1,
|
|
overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
|
}}>{z.name}</span>
|
|
|
|
{isGeschoss && (
|
|
<span style={{ fontSize: 10, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>
|
|
+{(z.okff ?? 0).toFixed(2)}
|
|
</span>
|
|
)}
|
|
|
|
{isGeschoss && <GeschossBadge name={z.name} />}
|
|
|
|
{isGeschoss && z.hasClipping && (
|
|
<Icon name="content_cut" size={12} style={{ color: 'var(--accent)', flexShrink: 0 }} title="Clipping Plane aktiv" />
|
|
)}
|
|
|
|
{eyeShown ? (
|
|
<button
|
|
className={`btn-icon-sm ${z.visible !== false ? 'is-on' : ''}`}
|
|
onClick={(ev) => { ev.stopPropagation(); onToggleVisible() }}
|
|
title={
|
|
active
|
|
? (z.visible !== false
|
|
? 'Normalerweise sichtbar (aktive Zeichnungsebene wird trotzdem gezeigt)'
|
|
: 'Normalerweise ausgeblendet — wird nur sichtbar weil aktiv')
|
|
: (z.visible !== false ? 'Ausblenden' : 'Einblenden')
|
|
}
|
|
><Icon name={z.visible !== false ? 'visibility' : 'visibility_off'} size={14} /></button>
|
|
) : (
|
|
<span style={{ width: 18, flexShrink: 0 }} />
|
|
)}
|
|
|
|
<button
|
|
className="btn-icon-xs"
|
|
onClick={(ev) => { ev.stopPropagation(); onSettings() }}
|
|
title="Einstellungen"
|
|
><Icon name="settings" size={12} /></button>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const MODES = [
|
|
{ value: 'all', label: 'Alle anzeigen' },
|
|
{ value: 'active', label: 'Nur aktive' },
|
|
{ value: 'grey', label: 'Andere grau' },
|
|
{ value: 'grey_locked', label: 'Andere grau & gesperrt' },
|
|
]
|
|
|
|
export default function GeschossManager({
|
|
zeichnungsebenen, activeId, onActiveChange, onChange, recalcOkff,
|
|
mode, onModeChange,
|
|
}) {
|
|
const [dialogOpen, setDialogOpen] = useState(false)
|
|
const [settingsFor, setSettingsFor] = useState(null) // Geschoss-Objekt oder null
|
|
|
|
const sorted = [...zeichnungsebenen].reverse()
|
|
const gesamthoehe = zeichnungsebenen
|
|
.filter(z => z.isGeschoss)
|
|
.reduce((s, z) => s + (z.hoehe ?? 0), 0)
|
|
|
|
const addQuick = () => {
|
|
const newZ = {
|
|
id: `z_${Date.now()}`,
|
|
name: `Neu ${zeichnungsebenen.length + 1}`,
|
|
isGeschoss: false,
|
|
visible: true,
|
|
}
|
|
onChange([...zeichnungsebenen, newZ])
|
|
}
|
|
|
|
const toggleVisible = (id) => {
|
|
onChange(zeichnungsebenen.map(z => z.id === id ? { ...z, visible: !(z.visible !== false) } : z))
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div style={{
|
|
display: 'flex', flexDirection: 'column', gap: 4,
|
|
padding: '6px 14px',
|
|
background: 'var(--bg-section)',
|
|
borderBottom: '1px solid var(--border-light)',
|
|
}}>
|
|
<span className="label-xs">Sichtbarkeit</span>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
<select
|
|
value={mode}
|
|
onChange={ev => onModeChange(ev.target.value)}
|
|
style={{ flex: 1, minWidth: 0 }}
|
|
>
|
|
{MODES.map(m => (
|
|
<option key={m.value} value={m.value}>{m.label}</option>
|
|
))}
|
|
</select>
|
|
<button className="btn-icon-sm" onClick={addQuick} title="Zeichnungsebene hinzufügen">
|
|
<Icon name="add" size={14} />
|
|
</button>
|
|
<button className="btn-icon-sm" onClick={() => setDialogOpen(true)} title="Bearbeiten">
|
|
<Icon name="edit" size={13} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{
|
|
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
|
padding: '3px 14px',
|
|
background: 'var(--bg-section)',
|
|
borderBottom: '1px solid var(--border-light)',
|
|
}}>
|
|
<span className="label-xs">Gebäudehöhe</span>
|
|
<span style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--text-secondary)' }}>
|
|
{gesamthoehe.toFixed(2)} m
|
|
</span>
|
|
</div>
|
|
|
|
<div>
|
|
{sorted.map(z => (
|
|
<ZeichnungsebeneRow
|
|
key={z.id}
|
|
z={z}
|
|
active={z.id === activeId}
|
|
mode={mode}
|
|
onClick={() => onActiveChange(z.id)}
|
|
onToggleVisible={() => toggleVisible(z.id)}
|
|
onSettings={() => setSettingsFor(z)}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{dialogOpen && (
|
|
<GeschossDialog
|
|
zeichnungsebenen={zeichnungsebenen}
|
|
recalcOkff={recalcOkff}
|
|
onSave={(updated) => { onChange(updated); setDialogOpen(false) }}
|
|
onClose={() => setDialogOpen(false)}
|
|
/>
|
|
)}
|
|
|
|
{settingsFor && (
|
|
<GeschossSettingsDialog
|
|
geschoss={settingsFor}
|
|
onSave={(updated) => {
|
|
onChange(zeichnungsebenen.map(z => z.id === updated.id ? updated : z))
|
|
setSettingsFor(null)
|
|
}}
|
|
onClose={() => setSettingsFor(null)}
|
|
/>
|
|
)}
|
|
</>
|
|
)
|
|
}
|