diff --git a/src/panels/AttributesPanel.tsx b/src/panels/AttributesPanel.tsx index 9f15a25..ff71513 100644 --- a/src/panels/AttributesPanel.tsx +++ b/src/panels/AttributesPanel.tsx @@ -28,6 +28,7 @@ import { formatM } from "../model/types"; import { PEN_WEIGHTS } from "../model/types"; import type { AttributeSource } from "../model/types"; import { usePanelHost } from "./host"; +import { Dropdown } from "../ui/Dropdown"; /** UI-Zustand des 3-Optionen-Quellen-Dropdowns (nicht 1:1 das Modell-Feld: * „custom" ist abgeleitet aus „ist ein expliziter Wert gesetzt?", nicht @@ -77,17 +78,17 @@ export function AttributesPanel() { ui: UiSource, onChange: (next: UiSource) => void, ) => ( - + onChange={(v) => onChange(v as UiSource)} + options={[ + { value: "layer", label: t("attr.source.layer") }, + { value: "object", label: t("attr.source.object") }, + { value: "custom", label: t("attr.source.custom") }, + ]} + /> ); // Farb-Swatch-Eingabe für „eigener Wert" (Vordergrund/Hintergrund). Erscheint @@ -246,19 +247,15 @@ export function AttributesPanel() { fillEditable, hatchUi, onHatchSourceChange, - , + onChange={(v) => host.onSetSelectionFill(v || null)} + options={[ + { value: "", label: t("attr.none") }, + ...project.hatches.map((h) => ({ value: h.id, label: h.name })), + ]} + />, )} diff --git a/src/panels/DisplayModeSelect.tsx b/src/panels/DisplayModeSelect.tsx index 200548f..e8d0a76 100644 --- a/src/panels/DisplayModeSelect.tsx +++ b/src/panels/DisplayModeSelect.tsx @@ -10,6 +10,7 @@ import type { DisplayMode } from "./types"; import { t } from "../i18n"; +import { Dropdown } from "../ui/Dropdown"; /** * Beschriftungs-Keys je Modus (Werte englisch, vgl. types.ts) — exakt die @@ -41,18 +42,12 @@ export function DisplayModeSelect({ }) { const label = title ?? t("display.title"); return ( - + onChange={(v) => onChange(v as DisplayMode)} + options={MODE_OPTIONS.map((o) => ({ value: o.value, label: t(o.labelKey) }))} + /> ); } diff --git a/src/panels/SitePanel.tsx b/src/panels/SitePanel.tsx index bdf86ce..f834c20 100644 --- a/src/panels/SitePanel.tsx +++ b/src/panels/SitePanel.tsx @@ -17,6 +17,7 @@ import type { TranslationKey } from "../i18n"; import { usePanelHost } from "./host"; import type { ContextObject } from "../model/types"; import { ContextImportDialog } from "../ui/ContextImportDialog"; +import { Dropdown } from "../ui/Dropdown"; /** Typ-spezifisches Badge-Label (i18n-Key) je Kontext-Objekt-Art. */ function typeBadgeKey(obj: ContextObject): TranslationKey { @@ -85,18 +86,12 @@ export function SitePanel() { {contourSets.length > 0 && (
{contourSets.length > 1 ? ( - + onChange={(v) => setSelectedSet(v)} + title={t("site.contourSet")} + options={contourSets.map((cs) => ({ value: cs.id, label: cs.name }))} + /> ) : ( {contourSets[0].name} diff --git a/src/panels/ToolsPanel.tsx b/src/panels/ToolsPanel.tsx index a21a197..4278df9 100644 --- a/src/panels/ToolsPanel.tsx +++ b/src/panels/ToolsPanel.tsx @@ -13,6 +13,7 @@ import { t } from "../i18n"; import { usePanelHost } from "./host"; import type { ToolId } from "./host"; import { TOOL_ORDER, getTool } from "../tools/tools"; +import { Dropdown } from "../ui/Dropdown"; // ── Icons ────────────────────────────────────────────────────────────────── // Keine echten Asset-Icons vorhanden: schlichte Inline-SVG-Glyphen je Werkzeug @@ -153,17 +154,15 @@ export function ToolsPanel() { {host.activeTool === "wall" && ( )} @@ -174,17 +173,15 @@ export function ToolsPanel() { {host.activeTool === "ceiling" && ( )} diff --git a/src/text/RichTextEditor.tsx b/src/text/RichTextEditor.tsx index 645bf10..f98c4b4 100644 --- a/src/text/RichTextEditor.tsx +++ b/src/text/RichTextEditor.tsx @@ -9,6 +9,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { t } from "../i18n"; import { docToHtml } from "./renderHtml"; +import { Dropdown } from "../ui/Dropdown"; import { applyMark, applyPreset, @@ -574,15 +575,13 @@ export function RichTextEditor({ ); const handlePreset = useCallback( - (e: React.ChangeEvent) => { - const id = e.target.value; + (id: string) => { const preset = effectivePresets.find((p) => p.id === id); if (!preset) return; const range = selRange ?? { start: 0, end: 0 }; const newDoc = applyPreset(valueRef.current, range, preset); onChangeRef.current(newDoc); setEditorHtml(newDoc, selRange, true); - e.target.value = ""; }, [effectivePresets, selRange, setEditorHtml], ); @@ -688,21 +687,16 @@ export function RichTextEditor({ {/* Presets */} {effectivePresets.length > 0 && ( - + options={[ + { value: "", label: `${t("rt.preset")} …`, disabled: true }, + ...effectivePresets.map((p) => ({ value: p.id, label: p.label })), + ]} + /> )}
diff --git a/src/ui/ImportDialog.tsx b/src/ui/ImportDialog.tsx index 5fb82ac..92dadbf 100644 --- a/src/ui/ImportDialog.tsx +++ b/src/ui/ImportDialog.tsx @@ -25,6 +25,7 @@ import { distinctLayers, } from "../io/dxfToDrawings"; import type { CategoryMode } from "../io/dxfToDrawings"; +import { Dropdown } from "./Dropdown"; /** Strukturierte Import-Entscheidung, die der Dialog beim Bestätigen liefert. */ export interface ImportDecision { @@ -226,18 +227,15 @@ export function ImportDialog({ {t("import.target.existing")} {targetKind === "existing" && ( - + onChange={(v) => setExistingLevelId(v)} + title={t("import.target.existing")} + options={project.drawingLevels.map((z) => ({ + value: z.id, + label: `${z.name} · ${t(kindBadgeKey(z.kind))}`, + }))} + /> )}