Panels poliert: Ebenenkombi in Oberleiste, Satelliten-Dialoge, Caps weg, Perf
- Ebenenkombination raus aus Ebenen-Panel, in Oberleiste-Topbar + Editor-Satellite (AusschnittLayerDialog embedded). doc.Strings haelt active_comb_name, auto-clear bei manueller Eye/Lock-Aenderung. - EbenenSettingsDialog jetzt Satellite mit Ebene-Picker-Dropdown (auto-save on switch via SAVE_KEEP). - Per-Ausschnitt Einstellungen-Satellite (Massstab, Display, Overrides, Ebenenkombi). Alte 'Sichtbarkeit bearbeiten'-Option entfernt. - Layouts/Ausschnitte: Top-Header weg, Sticky-Footer mit Anzahl + Aktionen. LayoutDialog ist jetzt Satellite mit Format-Live-Preview. - Panel-Captions + Default-Ebenen-Namen auf Mixed-Case (Ausschnitte, Ebenen, Waende ...). Nur DOSSIER bleibt caps. - DimensionenApp: Card-Optik raus, REF-Wuerfel mit Kreisen statt Quadraten + Hover-Scale. - GeschossManager angeglichen an EbenenManager: Rechtsklick-Menue, Lock-Button, Delete-X, Duplizieren. layer_builder honoriert z.locked. - Active Sublayer folgt jetzt dem Geschoss-Wechsel (gleicher Code unter neuem Parent). Performance Geschoss-Wechsel: - elemente._send_state() ersetzt durch _notify_active_geschoss() (Partial-Push statt 200+ Elements re-enumerieren). - _apply_visibility dedupe via sticky last-applied-signature (STATE_SYNC-Echo loopt nicht mehr durch alle Layer). - _update_clipping nur wenn alt oder neu hasClipping=True. - Redundante doc.Views.Redraw() im CPlane-Pfad entfernt — die folgende apply_visibility-Roundtrip redrawt 30ms spaeter ohnehin. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+53
-79
@@ -50,24 +50,22 @@ function NumInput({ value, onCommit, disabled, suffix, width }) {
|
||||
)
|
||||
}
|
||||
|
||||
// 9-Punkt-Referenzpunkt-Selektor im Illustrator-Stil: sichtbarer BBox-Rahmen,
|
||||
// die Punkte sitzen AUF Ecken / Kantenmitten / Zentrum.
|
||||
// 9-Punkt-Referenzpunkt-Selektor: sichtbarer BBox-Rahmen, Kreise auf den
|
||||
// Eckpunkten / Kantenmitten / Zentrum.
|
||||
function RefPointGrid({ ref, onChange }) {
|
||||
const SIZE = 26 // Aussenkanten-Quadrat (px)
|
||||
const DOT = 5 // Punkt-Durchmesser (px)
|
||||
// Position pro Code: 0% (min), 50% (mid), 100% (max)
|
||||
const SIZE = 28 // Aussenkanten-Quadrat (px)
|
||||
const DOT = 6 // Kreis-Durchmesser (px)
|
||||
const pct = (c) => c === 'min' ? '0%' : c === 'max' ? '100%' : '50%'
|
||||
return (
|
||||
<div style={{
|
||||
position: 'relative',
|
||||
width: SIZE, height: SIZE,
|
||||
border: '1px solid var(--text-muted)',
|
||||
border: '1px solid var(--border)',
|
||||
background: 'transparent',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
{REF_CODES.map(yc => REF_CODES.map(xc => {
|
||||
const active = ref.x === xc && ref.y === yc
|
||||
// yc 'max' = top in user mental model (Vectorworks/Illustrator)
|
||||
const topPct = yc === 'max' ? '0%' : yc === 'min' ? '100%' : '50%'
|
||||
return (
|
||||
<button
|
||||
@@ -79,14 +77,20 @@ function RefPointGrid({ ref, onChange }) {
|
||||
left: pct(xc), top: topPct,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: DOT, height: DOT, padding: 0,
|
||||
borderRadius: 0, // eckig wie Illustrator
|
||||
borderRadius: '50%',
|
||||
background: active ? 'var(--accent)' : 'var(--text-muted)',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.1s',
|
||||
transition: 'background 0.12s, transform 0.12s',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!active) e.currentTarget.style.background = 'var(--text-primary)'
|
||||
e.currentTarget.style.transform = 'translate(-50%, -50%) scale(1.25)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!active) e.currentTarget.style.background = 'var(--text-muted)'
|
||||
e.currentTarget.style.transform = 'translate(-50%, -50%) scale(1)'
|
||||
}}
|
||||
onMouseEnter={(e) => { if (!active) e.currentTarget.style.background = 'var(--text-primary)' }}
|
||||
onMouseLeave={(e) => { if (!active) e.currentTarget.style.background = 'var(--text-muted)' }}
|
||||
/>
|
||||
)
|
||||
}))}
|
||||
@@ -189,15 +193,13 @@ export default function DimensionenApp() {
|
||||
background: 'var(--bg-base)', color: 'var(--text-primary)',
|
||||
fontFamily: 'var(--font)', fontSize: 11,
|
||||
}}>
|
||||
<div style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden', padding: 6 }}>
|
||||
<div style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden' }}>
|
||||
|
||||
{/* Header: Selektions-Info + World/CPlane */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6, marginBottom: 6,
|
||||
padding: '5px 8px',
|
||||
background: 'var(--bg-section)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '8px 12px',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<Icon name="select_all" size={14} style={{ color: 'var(--text-muted)' }} />
|
||||
<span style={{ flex: 1, fontWeight: 500 }}>{selLabel()}</span>
|
||||
@@ -221,11 +223,8 @@ export default function DimensionenApp() {
|
||||
<div style={{
|
||||
padding: '32px 16px', textAlign: 'center',
|
||||
color: 'var(--text-muted)', fontSize: 11,
|
||||
border: '1px dashed var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
background: 'var(--bg-section)',
|
||||
}}>
|
||||
<Icon name="aspect_ratio" size={32} style={{ color: 'var(--text-muted)', opacity: 0.5 }} />
|
||||
<Icon name="aspect_ratio" size={32} style={{ color: 'var(--text-muted)', opacity: 0.4 }} />
|
||||
<div style={{ marginTop: 8 }}>Keine Selektion.</div>
|
||||
<div style={{ marginTop: 4, fontSize: 10 }}>
|
||||
In Rhino ein oder mehrere Objekte auswählen.
|
||||
@@ -233,38 +232,30 @@ export default function DimensionenApp() {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Referenzpunkt — kompakte einzeilige Card */}
|
||||
{/* Referenzpunkt */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
padding: '6px 8px', marginBottom: 6,
|
||||
background: 'var(--bg-section)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
display: 'flex', alignItems: 'center', gap: 10,
|
||||
padding: '10px 12px',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<span style={{ fontSize: 10, fontWeight: 600, color: 'var(--text-muted)',
|
||||
letterSpacing: '0.06em', textTransform: 'uppercase' }}>
|
||||
Ref
|
||||
</span>
|
||||
<span className="label-xs" style={{ width: 30 }}>Ref</span>
|
||||
<RefPointGrid ref={ref} onChange={onRefChange} />
|
||||
<div style={{ flex: 1 }} />
|
||||
<RefZSelector z={ref.z} onChange={(z) => onRefChange({ ...ref, z })} />
|
||||
</div>
|
||||
|
||||
{/* Position + Abmessungen nebeneinander */}
|
||||
<div style={{ display: 'flex', gap: 6, marginBottom: 6 }}>
|
||||
<div style={{
|
||||
flex: 1, display: 'flex', flexDirection: 'column', gap: 4,
|
||||
padding: '6px 8px',
|
||||
background: 'var(--bg-section)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
minWidth: 0,
|
||||
}}>
|
||||
<div style={{ fontSize: 10, fontWeight: 600, color: 'var(--text-muted)',
|
||||
letterSpacing: '0.06em', textTransform: 'uppercase',
|
||||
display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>Position</span>
|
||||
<span style={{ fontWeight: 400, textTransform: 'none',
|
||||
fontFamily: 'DM Mono, monospace', fontSize: 9 }}>
|
||||
{/* Position + BBox nebeneinander */}
|
||||
<div style={{
|
||||
display: 'flex', gap: 16,
|
||||
padding: '10px 12px',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between',
|
||||
alignItems: 'baseline', marginBottom: 2 }}>
|
||||
<span className="label-xs">Position</span>
|
||||
<span style={{ fontFamily: 'DM Mono, monospace', fontSize: 9,
|
||||
color: 'var(--text-muted)' }}>
|
||||
{state.planeName}
|
||||
</span>
|
||||
</div>
|
||||
@@ -272,18 +263,9 @@ export default function DimensionenApp() {
|
||||
<Field label="Y"><NumInput value={pos.y} onCommit={(v) => setDimPosition('y', v)} /></Field>
|
||||
<Field label="Z"><NumInput value={pos.z} onCommit={(v) => setDimPosition('z', v)} /></Field>
|
||||
</div>
|
||||
<div style={{
|
||||
flex: 1, display: 'flex', flexDirection: 'column', gap: 4,
|
||||
padding: '6px 8px',
|
||||
background: 'var(--bg-section)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
minWidth: 0,
|
||||
}}>
|
||||
<div style={{ fontSize: 10, fontWeight: 600, color: 'var(--text-muted)',
|
||||
letterSpacing: '0.06em', textTransform: 'uppercase' }}>
|
||||
BBox
|
||||
</div>
|
||||
<div style={{ width: 1, background: 'var(--border-light)' }} />
|
||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0 }}>
|
||||
<span className="label-xs" style={{ marginBottom: 2 }}>BBox</span>
|
||||
<Field label="B"><NumInput value={dims?.width} onCommit={(v) => setDimDimension('width', v)} /></Field>
|
||||
<Field label="T"><NumInput value={dims?.depth} onCommit={(v) => setDimDimension('depth', v)} /></Field>
|
||||
<Field label="H"><NumInput value={dims?.height} onCommit={(v) => setDimDimension('height', v)} /></Field>
|
||||
@@ -294,22 +276,19 @@ export default function DimensionenApp() {
|
||||
{shape && (
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', gap: 4,
|
||||
padding: '6px 8px', marginBottom: 6,
|
||||
background: 'var(--bg-section)',
|
||||
border: '1px solid var(--accent)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
padding: '10px 12px',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<div style={{ fontSize: 10, fontWeight: 600, color: 'var(--accent)',
|
||||
letterSpacing: '0.06em', textTransform: 'uppercase' }}>
|
||||
<span className="label-xs" style={{ color: 'var(--accent)' }}>
|
||||
{shape.type === 'circle' && 'Kreis'}
|
||||
{shape.type === 'rectangle' && 'Rechteck'}
|
||||
{shape.type === 'line' && 'Linie'}
|
||||
</div>
|
||||
</span>
|
||||
{shape.type === 'circle' && (
|
||||
<Field label="R"><NumInput value={shape.radius} onCommit={(v) => setCircleRadius(v)} /></Field>
|
||||
)}
|
||||
{shape.type === 'rectangle' && (
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<Field label="W" style={{ flex: 1 }}>
|
||||
<NumInput value={shape.width}
|
||||
onCommit={(v) => setRectangleDims(v, shape.height)} />
|
||||
@@ -321,7 +300,7 @@ export default function DimensionenApp() {
|
||||
</div>
|
||||
)}
|
||||
{shape.type === 'line' && (
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<Field label="L" style={{ flex: 1 }}>
|
||||
<NumInput value={shape.length} onCommit={(v) => setLineLength(v)} />
|
||||
</Field>
|
||||
@@ -333,19 +312,13 @@ export default function DimensionenApp() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Rotation — kompakt einzeilig */}
|
||||
{/* Rotation */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '6px 8px', marginBottom: 6,
|
||||
background: 'var(--bg-section)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
padding: '10px 12px',
|
||||
}}>
|
||||
<span style={{ fontSize: 10, fontWeight: 600, color: 'var(--text-muted)',
|
||||
letterSpacing: '0.06em', textTransform: 'uppercase' }}>
|
||||
Drehen
|
||||
</span>
|
||||
<div style={{ flex: 1 }}>
|
||||
<span className="label-xs" style={{ width: 50 }}>Drehen</span>
|
||||
<div style={{ width: 56 }}>
|
||||
<NumInput value={rotationDelta} onCommit={setRotationDelta} suffix="°" />
|
||||
</div>
|
||||
<button
|
||||
@@ -357,12 +330,13 @@ export default function DimensionenApp() {
|
||||
>
|
||||
<Icon name="rotate_right" size={13} />
|
||||
</button>
|
||||
<div style={{ flex: 1 }} />
|
||||
{[-90, -45, 45, 90].map(a => (
|
||||
<button
|
||||
key={a}
|
||||
className="btn-outlined"
|
||||
onClick={() => setDimRotationZ(a)}
|
||||
style={{ padding: '3px 5px', fontSize: 9, minWidth: 28 }}
|
||||
style={{ padding: '3px 6px', fontSize: 9, minWidth: 28 }}
|
||||
title={`${a}°`}
|
||||
>
|
||||
{a > 0 ? '+' : ''}{a}°
|
||||
|
||||
Reference in New Issue
Block a user