Fenster/Tueren LoD + Stile + Phase-3-Ausschnitt-Darstellung + UI-Konsistenz
Fenster/Tueren: - 3-stufige SIA-400-Darstellung pro Element: einfach (1:100, flache Scheibe ohne Tiefe in Wand-Mittelebene), standard (1:50, Rahmen + Glas + Sims), detail (1:20, Doppelverglasung). - Aussenseite-Flag mit Auto-Detection aus der Click-Richtung beim Setzen — Sim sitzt automatisch aussen. Im Panel als Umkehren-Toggle. - Tueren-Rahmen-Typ Zarge|Block — Blockrahmen ragt seitlich raus. - Rahmen-Offset (m von Wand-Innenseite) ersetzt das 3-Preset Lage- Feld. Wirkt auch in der einfachen Darstellung (Pane sitzt auf der Rahmen-Mittelebene, nicht in Wand-Mitte). - Sims nur AUSSEN. Innen entfaellt — der Sim ist gleichzeitig der visuelle Indikator fuer die Aussenseite. - Oeffnungs-Stile: list/save/delete-API mit 6 Default-Presets (Fenster Standard/Gross/Bandlage, Tuer Innen/Eingang/Verglast). Style-ID per UserString am Objekt persistiert. Im Panel BarCombo mit "Aktuelle als Stil speichern…". Beim Rhino-Command "Stil"- Option zum Picken vor dem Klick. Ausschnitt-Darstellung (Phase 3): - Doc-Level Override dossier_aktive_darstellung gewinnt vor per- Object-Setting. Wechsel triggert Regen aller Oeffnungen via neuer regenerate_all_oeffnungen-API. - Ausschnitt-Capture speichert die Darstellung mit, Restore wendet sie an und regeneriert. - Oberleiste-Quick-Switch BarCombo mit 4 Optionen. - AusschnittSettings-Dialog: Darstellungs-Dropdown. Gestaltung (SectionStyle Phase 2): - _set_section_style schreibt per-Object SectionHatchIndex/Scale/ Rotation/Color mit Multi-Fallback (Property-Namen varieren je Rhino-Build). _selection_summary liest die selben zurueck. - HatchEditor als shared Component fuer Fill + Section. - geometryKind ignoriert DOSSIER-Source-Curves damit Wand-Selektion (Axis + Volume) als 3D klassifiziert wird. UI-Konsistenz Panels: - Ebenenkombi zurueck als eigene Section oben im Ebenen-Panel, Modelldarstellung-Dropdown an die freigewordene Position in der Oberleiste (Row 1 Col 2 im 2x2-Preset-Block). - BarCombo erweitert: stretch-Prop (Pill waechst auf Container- Breite), onSecond/secondIcon/secondTitle fuer 2. Trailing-Button, gearIcon-Prop. Plus-Slot immer ganz aussen rechts, Settings-Slot direkt nach dem Caret. - Ebenen + Zeichnungsebenen visuell kohaerent: identisches Padding (1px 12px 1px 0), Chevron/Spacer-Slot 12px, Master-Row mit Eye 16x16 + Lock 14x14, gleiche Border + Borderfarbe. Eye-Icons in beiden Panels untereinander ausgerichtet. - Properties-Container ohne Border (war zuvor accent-gruen, dann border — User wollte gar nichts mehr). - ElementList raus aus dem Elemente-Panel (Uebersicht via Tree- Window erreichbar). NeuesElement bleibt voll sichtbar bei Selektion (kein Collapse), Properties oben. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,9 @@ import Icon from './Icon'
|
||||
import ConfirmDeleteEbene from './ConfirmDeleteEbene'
|
||||
import ContextMenu from './ContextMenu'
|
||||
import { BarCombo, BarButton } from './BarControls'
|
||||
import { setLayerStyle, deleteEbene, moveSelectionToEbene, openEbenenSettings } from '../lib/rhinoBridge'
|
||||
import { setLayerStyle, deleteEbene, moveSelectionToEbene, openEbenenSettings,
|
||||
pickLayerCombination, saveLayerCombination, deleteLayerCombination,
|
||||
openLayerCombinationsDialog } from '../lib/rhinoBridge'
|
||||
|
||||
const MODES = [
|
||||
{ value: 'all_force', label: 'Alle anzeigen' },
|
||||
@@ -245,8 +247,8 @@ function EbeneRow({ e, depth, hasChildren, expanded, onToggleExpand, active, mod
|
||||
onContextMenu={onContextMenu}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 4,
|
||||
padding: '1px 8px',
|
||||
paddingLeft: 6 + (depth || 0) * 10,
|
||||
padding: '1px 12px 1px 0',
|
||||
paddingLeft: (depth || 0) * 12,
|
||||
margin: 0,
|
||||
background: active ? 'var(--active-dim)'
|
||||
: (e.visible !== false) ? 'var(--bg-item)'
|
||||
@@ -345,6 +347,7 @@ function SortHeader({ label, sortKey, sortBy, sortDir, onSort, style }) {
|
||||
|
||||
export default function EbenenManager({
|
||||
ebenen, activeCode, onActiveChange, onChange, mode, onModeChange, hatchPatterns,
|
||||
layerCombinations = [], activeKombi = null,
|
||||
}) {
|
||||
const [sortBy, setSortBy] = useState('code')
|
||||
const [sortDir, setSortDir] = useState('asc')
|
||||
@@ -548,70 +551,114 @@ export default function EbenenManager({
|
||||
background: 'var(--bg-section)',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<span className="label-xs">Sichtbarkeit</span>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<div style={{ flex: 1, minWidth: 0, display: 'flex' }}>
|
||||
<BarCombo
|
||||
icon="visibility"
|
||||
value={mode}
|
||||
onChange={onModeChange}
|
||||
title="Sichtbarkeits-Modus"
|
||||
>
|
||||
{MODES.map(m => <option key={m.value} value={m.value}>{m.label}</option>)}
|
||||
</BarCombo>
|
||||
</div>
|
||||
<BarButton icon="add" onClick={addNew} title="Ebene hinzufügen" />
|
||||
<span className="label-xs">Ebenenkombination</span>
|
||||
<div style={{ display: 'flex', width: '100%' }}>
|
||||
<BarCombo
|
||||
stretch
|
||||
icon="layers"
|
||||
value={activeKombi || '__none__'}
|
||||
onChange={(v) => {
|
||||
if (v === '__configure__') { openLayerCombinationsDialog(); return }
|
||||
if (v === '__save__') {
|
||||
const suggested = activeKombi || `Kombi ${layerCombinations.length + 1}`
|
||||
const name = (window.prompt('Name für Ebenenkombination:', suggested) || '').trim()
|
||||
if (!name) return
|
||||
if (layerCombinations.includes(name) &&
|
||||
!window.confirm(`"${name}" überschreiben?`)) return
|
||||
saveLayerCombination(name)
|
||||
return
|
||||
}
|
||||
if (v === '__delete__') {
|
||||
if (activeKombi &&
|
||||
window.confirm(`Kombination "${activeKombi}" löschen?`))
|
||||
deleteLayerCombination(activeKombi)
|
||||
return
|
||||
}
|
||||
pickLayerCombination(v === '__none__' ? null : v)
|
||||
}}
|
||||
title={activeKombi
|
||||
? `Aktive Kombi: ${activeKombi}`
|
||||
: 'Keine Kombination — manuelle Sichtbarkeit'}
|
||||
onGear={openLayerCombinationsDialog}
|
||||
gearTitle="Ebenenkombinationen bearbeiten"
|
||||
>
|
||||
<option value="__none__">— Eigene —</option>
|
||||
{layerCombinations.map(n => (
|
||||
<option key={n} value={n}>{n}</option>
|
||||
))}
|
||||
<option disabled>──────────</option>
|
||||
<option value="__save__">+ Aktuelle speichern…</option>
|
||||
{activeKombi && (
|
||||
<option value="__delete__">🗑 Aktuelle löschen</option>
|
||||
)}
|
||||
<option value="__configure__">Bearbeiten…</option>
|
||||
</BarCombo>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 4,
|
||||
padding: '2px 8px 2px 9px',
|
||||
display: 'flex', flexDirection: 'column', gap: 4,
|
||||
padding: '6px 14px',
|
||||
background: 'var(--bg-section)',
|
||||
borderBottom: '1px solid var(--border)',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
{/* Master-Eye: alle Ebenen sichtbar/unsichtbar */}
|
||||
<span className="label-xs">Sichtbarkeit</span>
|
||||
<div style={{ display: 'flex', width: '100%' }}>
|
||||
<BarCombo
|
||||
stretch
|
||||
icon="visibility"
|
||||
value={mode}
|
||||
onChange={onModeChange}
|
||||
title="Sichtbarkeits-Modus"
|
||||
onSecond={addNew}
|
||||
secondIcon="add"
|
||||
secondTitle="Ebene hinzufügen"
|
||||
>
|
||||
{MODES.map(m => <option key={m.value} value={m.value}>{m.label}</option>)}
|
||||
</BarCombo>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sort-Header-Row + Master-Eye/Lock. Padding-Left identisch zu
|
||||
den Data-Rows damit Eye-Icons aligned sind. Erste 12px-Spanne
|
||||
spiegelt den Expand-Chevron-Slot der Data-Rows wider. */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 4,
|
||||
padding: '2px 12px 2px 0',
|
||||
background: 'var(--bg-section)',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<span style={{ width: 12, flexShrink: 0 }} />
|
||||
<button
|
||||
className="btn-icon-xs"
|
||||
onClick={() => {
|
||||
const anyVisible = ebenen.some(e => e.visible !== false)
|
||||
// Wenn irgendeine sichtbar -> alle aus. Wenn keine sichtbar -> alle an.
|
||||
onChange(ebenen.map(e => ({ ...e, visible: !anyVisible })))
|
||||
if (mode === 'active' || mode === 'all_force') onModeChange('all')
|
||||
}}
|
||||
title={ebenen.every(e => e.visible !== false)
|
||||
? 'Alle Ebenen ausblenden'
|
||||
: 'Alle Ebenen einblenden'}
|
||||
style={{ width: 18, height: 18,
|
||||
? 'Alle Ebenen ausblenden' : 'Alle Ebenen einblenden'}
|
||||
style={{ width: 16, height: 16,
|
||||
opacity: (mode === 'active' || mode === 'all_force') ? 0.5 : 1 }}
|
||||
>
|
||||
<Icon
|
||||
name={ebenen.every(e => e.visible !== false) ? 'visibility' : 'visibility_off'}
|
||||
size={12}
|
||||
/>
|
||||
<Icon name={ebenen.every(e => e.visible !== false) ? 'visibility' : 'visibility_off'} size={11} />
|
||||
</button>
|
||||
<SortHeader label="Cd" sortKey="code" sortBy={sortBy} sortDir={sortDir} onSort={toggleSort} style={{ width: 24 }} />
|
||||
<SortHeader label="N" sortKey="code" sortBy={sortBy} sortDir={sortDir} onSort={toggleSort} style={{ width: 22 }} />
|
||||
<div style={{ width: 12 }} />
|
||||
<SortHeader label="Name" sortKey="name" sortBy={sortBy} sortDir={sortDir} onSort={toggleSort} style={{ flex: 1 }} />
|
||||
<SortHeader label="Lw" sortKey="lw" sortBy={sortBy} sortDir={sortDir} onSort={toggleSort} style={{ width: 42, textAlign: 'right', display: 'block' }} />
|
||||
{/* Master-Lock: alle Ebenen sperren/entsperren */}
|
||||
<button
|
||||
className="btn-icon-xs"
|
||||
onClick={() => {
|
||||
const anyLocked = ebenen.some(e => e.locked === true)
|
||||
onChange(ebenen.map(e => ({ ...e, locked: !anyLocked })))
|
||||
}}
|
||||
title={ebenen.every(e => e.locked === true)
|
||||
? 'Alle Ebenen entsperren'
|
||||
: 'Alle Ebenen sperren'}
|
||||
style={{ width: 18, height: 18 }}
|
||||
title={ebenen.every(e => e.locked === true) ? 'Alle Ebenen entsperren' : 'Alle Ebenen sperren'}
|
||||
style={{ width: 14, height: 14 }}
|
||||
>
|
||||
<Icon
|
||||
name={ebenen.every(e => e.locked === true) ? 'lock' : 'lock_open'}
|
||||
size={11}
|
||||
/>
|
||||
<Icon name={ebenen.every(e => e.locked === true) ? 'lock' : 'lock_open'} size={11} />
|
||||
</button>
|
||||
<div style={{ width: 18 }} />
|
||||
<div style={{ width: 14 }} />
|
||||
</div>
|
||||
|
||||
{(() => {
|
||||
|
||||
Reference in New Issue
Block a user