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))}`,
+ }))}
+ />
)}