Satelliten-Dialoge: embedded-Mode + GeschossDialog auch als echtes Fenster
Zwei Dinge: 1. embedded-Mode in den Dialog-Komponenten — wenn TRUE, kein Backdrop + keine MaxWidth-Constraint, das Dialog fuellt das ganze WebView statt wie ein kleines zentriertes Fenster IN dem WebView gerendert zu werden (= "Fenster im Fenster"-Effekt). Betroffen: - GeschossSettingsDialog - EbenenSettingsDialog - GeschossDialog Satelliten-Apps (GeschossSettingsApp, EbenenSettingsApp, GeschossDialogApp) passen jetzt `embedded` durch. 2. GeschossDialog (= der grosse Mehrfach-Editor hinter dem Pencil-Button) laeuft jetzt auch als Satelliten-Fenster — selbe Architektur wie die Settings-Dialoge. Backend hat neuen Handler _open_geschoss_dialog und neuen Message OPEN_GESCHOSS_DIALOG. Auf Save: ganze z-Liste replace + _apply(save_z=True). GeschossManager braucht den inline-Dialog-State nicht mehr; Pencil-Button ruft openGeschossDialog(zeichnungsebenen). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,7 @@ function SectionLabel({ children }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default function EbenenSettingsDialog({ ebene, hatchPatterns = ['Solid'], onSave, onClose }) {
|
||||
export default function EbenenSettingsDialog({ ebene, hatchPatterns = ['Solid'], onSave, onClose, embedded = false }) {
|
||||
const [draft, setDraft] = useState({
|
||||
...ebene,
|
||||
fill: {
|
||||
@@ -78,23 +78,35 @@ export default function EbenenSettingsDialog({ ebene, hatchPatterns = ['Solid'],
|
||||
...hatchPatterns.filter(p => p !== 'Solid' && p !== 'None'),
|
||||
]
|
||||
|
||||
const wrapperStyle = embedded ? {
|
||||
width: '100%', height: '100%',
|
||||
background: 'var(--bg-dialog)',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
} : {
|
||||
position: 'absolute', inset: 0, zIndex: 150,
|
||||
background: 'var(--bg-overlay)',
|
||||
display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
|
||||
paddingTop: 30,
|
||||
}
|
||||
const innerStyle = embedded ? {
|
||||
width: '100%', height: '100%',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
} : {
|
||||
background: 'var(--bg-dialog)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
boxShadow: 'var(--shadow-3)',
|
||||
width: 'calc(100% - 16px)', maxWidth: 300,
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
maxHeight: 'calc(100vh - 60px)',
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, zIndex: 150,
|
||||
background: 'var(--bg-overlay)',
|
||||
display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
|
||||
paddingTop: 30,
|
||||
}}>
|
||||
<div style={{
|
||||
background: 'var(--bg-dialog)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
boxShadow: 'var(--shadow-3)',
|
||||
width: 'calc(100% - 16px)', maxWidth: 300,
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
maxHeight: 'calc(100vh - 60px)',
|
||||
}}>
|
||||
<div style={wrapperStyle}>
|
||||
<div style={innerStyle}>
|
||||
{/* Header */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import Icon from './Icon'
|
||||
|
||||
export default function GeschossDialog({ zeichnungsebenen, recalcOkff, onSave, onClose }) {
|
||||
export default function GeschossDialog({ zeichnungsebenen, recalcOkff, onSave, onClose, embedded = false }) {
|
||||
const [draft, setDraft] = useState(zeichnungsebenen.map(z => ({ ...z })))
|
||||
|
||||
const update = (i, field, val) => {
|
||||
@@ -56,23 +56,34 @@ export default function GeschossDialog({ zeichnungsebenen, recalcOkff, onSave, o
|
||||
del: { width: 22, flexShrink: 0 },
|
||||
}
|
||||
|
||||
const wrapperStyle = embedded ? {
|
||||
width: '100%', height: '100%',
|
||||
background: 'var(--bg-dialog)',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
} : {
|
||||
position: 'absolute', inset: 0, zIndex: 100,
|
||||
background: 'var(--bg-overlay)',
|
||||
display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
|
||||
paddingTop: 40,
|
||||
}
|
||||
const innerStyle = embedded ? {
|
||||
width: '100%', height: '100%',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
} : {
|
||||
background: 'var(--bg-dialog)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
width: 'calc(100% - 24px)',
|
||||
boxShadow: 'var(--shadow)',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
maxHeight: 'calc(100vh - 80px)',
|
||||
overflow: 'hidden',
|
||||
}
|
||||
return (
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, zIndex: 100,
|
||||
background: 'var(--bg-overlay)',
|
||||
display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
|
||||
paddingTop: 40,
|
||||
}}>
|
||||
<div style={{
|
||||
background: 'var(--bg-dialog)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
width: 'calc(100% - 24px)',
|
||||
boxShadow: 'var(--shadow)',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
maxHeight: 'calc(100vh - 80px)',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<div style={wrapperStyle}>
|
||||
<div style={innerStyle}>
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
padding: '10px 14px', borderBottom: '1px solid var(--border)', flexShrink: 0,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import Icon from './Icon'
|
||||
import GeschossDialog from './GeschossDialog'
|
||||
import { openGeschossSettings } from '../lib/rhinoBridge'
|
||||
import { openGeschossSettings, openGeschossDialog } from '../lib/rhinoBridge'
|
||||
|
||||
function GeschossBadge({ name }) {
|
||||
return <span className="chip chip-info">{name}</span>
|
||||
@@ -85,7 +83,8 @@ export default function GeschossManager({
|
||||
zeichnungsebenen, activeId, onActiveChange, onChange, recalcOkff,
|
||||
mode, onModeChange,
|
||||
}) {
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
// dialogOpen-State entfaellt — Bearbeiten-Dialog laeuft jetzt als
|
||||
// Satelliten-Fenster via openGeschossDialog().
|
||||
|
||||
const sorted = [...zeichnungsebenen].reverse()
|
||||
const gesamthoehe = zeichnungsebenen
|
||||
@@ -134,7 +133,7 @@ export default function GeschossManager({
|
||||
<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">
|
||||
<button className="btn-icon-sm" onClick={() => openGeschossDialog(zeichnungsebenen)} title="Bearbeiten">
|
||||
<Icon name="edit" size={13} />
|
||||
</button>
|
||||
</div>
|
||||
@@ -166,15 +165,6 @@ export default function GeschossManager({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{dialogOpen && (
|
||||
<GeschossDialog
|
||||
zeichnungsebenen={zeichnungsebenen}
|
||||
recalcOkff={recalcOkff}
|
||||
onSave={(updated) => { onChange(updated); setDialogOpen(false) }}
|
||||
onClose={() => setDialogOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ function Toggle({ label, checked, onChange, hint }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default function GeschossSettingsDialog({ geschoss, onSave, onClose }) {
|
||||
export default function GeschossSettingsDialog({ geschoss, onSave, onClose, embedded = false }) {
|
||||
const [draft, setDraft] = useState({ ...geschoss })
|
||||
const set = (patch) => setDraft({ ...draft, ...patch })
|
||||
|
||||
@@ -46,22 +46,37 @@ export default function GeschossSettingsDialog({ geschoss, onSave, onClose }) {
|
||||
const okff = draft.okff ?? 0
|
||||
const clipZ = (okff + schnitt).toFixed(2)
|
||||
|
||||
// embedded=true: in einem Satelliten-Fenster gerendert — kein Backdrop,
|
||||
// keine Width-Constraint, fuellt das ganze WebView.
|
||||
const Wrapper = embedded ? 'div' : 'div'
|
||||
const wrapperStyle = embedded ? {
|
||||
width: '100%', height: '100%',
|
||||
background: 'var(--bg-dialog)',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
} : {
|
||||
position: 'absolute', inset: 0, zIndex: 150,
|
||||
background: 'var(--bg-overlay)',
|
||||
display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
|
||||
paddingTop: 30,
|
||||
}
|
||||
const innerStyle = embedded ? {
|
||||
width: '100%', height: '100%',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
} : {
|
||||
background: 'var(--bg-dialog)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
boxShadow: 'var(--shadow-3)',
|
||||
width: 'calc(100% - 16px)', maxWidth: 280,
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, zIndex: 150,
|
||||
background: 'var(--bg-overlay)',
|
||||
display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
|
||||
paddingTop: 30,
|
||||
}}>
|
||||
<div style={{
|
||||
background: 'var(--bg-dialog)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
boxShadow: 'var(--shadow-3)',
|
||||
width: 'calc(100% - 16px)', maxWidth: 280,
|
||||
display: 'flex', flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Wrapper style={wrapperStyle}>
|
||||
<div style={innerStyle}>
|
||||
{/* Header */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
@@ -155,6 +170,6 @@ export default function GeschossSettingsDialog({ geschoss, onSave, onClose }) {
|
||||
}}>Übernehmen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user