Raumstempel-Stile (Presets) — speichern + anwenden per Doc

Damit User wiederkehrende Stempel-Configs (Wettbewerb / Bauantrag /
Mobiliar etc.) nicht jedes Mal neu klicken muss.

Backend (elemente.py):
- Storage in doc.Strings dossier_raum_stempel_stile als JSON-Array
- load_raum_stempel_stile / save_raum_stempel_stile Helpers
- Bridge-Handler:
  - SAVE_RAUM_STIL: upsert by id (neu wenn leer)
  - DELETE_RAUM_STIL: remove by id
  - APPLY_RAUM_STIL: schreibt Stil-Felder auf Raum-IDs + Regen
- _RAUM_STIL_FIELDS umfasst font/bold/italic/txtH/txtModus/align/
  rundung/fuellung/showSia/layout (alles was die Optik bestimmt)
- raumStempelStile im STATE-Emit zum Frontend

Frontend:
- saveRaumStil/deleteRaumStil/applyRaumStil in rhinoBridge.js
- RaumProperties: neue "Stil"-Sektion oben mit Dropdown
  * gespeicherte Stile + "+ Aktuelle Settings als Stil speichern" +
    "🗑 Aktiven Stil loeschen"
  * Klick auf Stil → applyRaumStil mit aktueller Raum-ID
- Beide PropertiesView-Aufrufe (inline + satellite) bekommen die Liste
This commit is contained in:
2026-05-26 23:17:08 +02:00
parent da0fd365f2
commit f1860ae85d
4 changed files with 215 additions and 3 deletions
+58 -3
View File
@@ -11,6 +11,7 @@ import {
openSwisstopo, openSwisstopoDialog, openOsmDialog,
updateElement, deleteElement, openElementeUebersicht, openElementeProperties,
saveOeffStyle, deleteOeffStyle,
saveRaumStil, deleteRaumStil, applyRaumStil,
listLibrary,
} from './lib/rhinoBridge'
@@ -506,7 +507,7 @@ function NeuesElementSection({ noGeschoss, activeName, elementsCount }) {
// PropertiesView: gemeinsame Komponente, rendert die passende Property-
// Form je nach Element-Typ. Wiederverwendbar in Inline + Satellite-Window.
export function PropertiesView({ selected, geschosse, materials, hatchPatterns, oeffStyles, fonts }) {
export function PropertiesView({ selected, geschosse, materials, hatchPatterns, oeffStyles, fonts, raumStempelStile }) {
if (!selected) return null
const upd = (p) => updateElement(selected.id, p)
const del = (label) => () => { if (window.confirm(`${label} löschen?`)) deleteElement(selected.id) }
@@ -529,6 +530,7 @@ export function PropertiesView({ selected, geschosse, materials, hatchPatterns,
if (selected.kind === 'raum')
return <RaumProperties raum={selected} geschosse={geschosse}
hatchPatterns={hatchPatterns} fonts={fonts || []}
raumStempelStile={raumStempelStile || []}
onUpdate={upd} onDelete={del('Raum')} />
if (selected.kind === 'aussparung')
@@ -583,7 +585,8 @@ export default function ElementeApp() {
materials={state.materials || []}
hatchPatterns={state.hatchPatterns}
fonts={state.fonts || []}
oeffStyles={state.oeffStyles || []} />
oeffStyles={state.oeffStyles || []}
raumStempelStile={state.raumStempelStile || []} />
</div>
)}
<NeuesElementSection
@@ -883,7 +886,40 @@ function StempelLayoutBuilder({ layout, availableFields, onChange }) {
)
}
function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fonts }) {
function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fonts, raumStempelStile }) {
const stilList = raumStempelStile || []
// Match: aktueller Raum-Stempel-Aktiv-Stil-id wird per UserString
// dossier_raum_stil_id gespeichert wenn ein Stil applied wurde. Fuer
// jetzt: nicht-persistent — Match anhand visueller Settings (font + layout)
// koennten wir tun, aber zu fragil. Default: kein Stil markiert.
const activeStilId = raum.stilId || ''
const handleStilChange = (val) => {
if (val === '__save__') {
const n = (window.prompt('Name für neuen Stempel-Stil:', 'Stil') || '').trim()
if (!n) return
saveRaumStil('', n, {
font: raum.font || '',
bold: !!raum.bold,
italic: !!raum.italic,
txtH: raum.txtH,
txtModus: raum.txtModus || 'fix',
align: raum.align || 'mid',
rundung: raum.rundung || '',
fuellung: raum.fuellung || '',
showSia: !!raum.showSia,
layout: Array.isArray(raum.layout) ? raum.layout : [],
})
return
}
if (val === '__delete__') {
if (activeStilId &&
window.confirm(`Stil "${stilList.find(s => s.id === activeStilId)?.name}" löschen?`))
deleteRaumStil(activeStilId)
return
}
if (val) applyRaumStil(val, [raum.id])
}
const [name, setName] = useState(raum.name || 'Raum')
const [nummer, setNummer] = useState(raum.nummer || '')
const [funktion, setFunktion] = useState(raum.funktion || '')
@@ -944,6 +980,25 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
</button>
</div>
{/* Stempel-Stil Preset-Picker — apply einer gespeicherten Visual-Vorlage
auf den Raum. "+ Aktuell speichern" frischt die Liste mit den
jetzigen Settings als neuen Stil. Stile sind per-Doc. */}
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Stil</span>
<select value={activeStilId}
onChange={(e) => handleStilChange(e.target.value)}
style={{ flex: 1, fontSize: 11 }}
title="Gespeicherter Stempel-Stil — Klick wendet die Vorlage an">
<option value=""> Stil wählen </option>
{stilList.map(s => (
<option key={s.id} value={s.id}>{s.name}</option>
))}
<option disabled></option>
<option value="__save__">+ Aktuelle Settings als Stil speichern</option>
{activeStilId && <option value="__delete__">🗑 Aktiven Stil löschen</option>}
</select>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Geschoss</span>
<select value={raum.geschoss}