diff --git a/docs/design/ribbon-ui-plan.md b/docs/design/ribbon-ui-plan.md index 6aa25d0..326dcde 100644 --- a/docs/design/ribbon-ui-plan.md +++ b/docs/design/ribbon-ui-plan.md @@ -52,12 +52,15 @@ RIBBON: RibbonTab[] Kamera, Ebenen-/Zeichnungs-Kombis, Detailgrad, Massstab/Zoom, Darstellungsart) sind als `ViewRibbonTab` (in `TopBar.tsx`) in den „Ansichten"-Tab gewandert; App reicht sie als `viewsContent`-Node an `RibbonBar` (wie das Layout-Menü). - Die TopBar ist jetzt die **schmale globale Leiste**: Marke/Ressourcen · - Text-Formatierung · Datei/Export/Einstellungen · Fensterknöpfe. - - **Offen (visuell iterieren):** Band-Höhe/Abstände des Ansichten-Tabs; ob - Zoom/Massstab zusätzlich als Dauer-Anzeige (Statusleiste) sichtbar sein - soll; Default-Tab (aktuell 2D); Text-Gruppe evtl. später eigener - „Beschriften"-Tab; 3D-Tab noch leer (Kamera-Presets/3D-Aktionen füllen). + Die **Tab-Reiter sitzen in der TopBar-Zeile** (`RibbonTabs`, in TopBar + gerendert; Tab-State in App), der Ribbon-Inhalt (`RibbonBar`) folgt darunter → + nur ZWEI Bänder. Die TopBar ist eine **schmale Leiste** (Höhe 40px): Marke/ + Ressourcen · Tabs · Datei/Export/Einstellungen · Fensterknöpfe. Die Text-/ + Font-Formatierung (`TextGroup`) liegt im eigenen **„Text"-Tab** (Reihenfolge + 2D·3D·BIM·Text·Ansichten; `RibbonBar.tabContent` trägt „text" + „views"). + - **Offen (visuell iterieren):** ob Zoom/Massstab zusätzlich als Dauer-Anzeige + (Statusleiste) sichtbar sein soll; Default-Tab (aktuell 2D); 3D-Tab noch + leer (Kamera-Presets/3D-Aktionen füllen). 3. **Sidebar raus** (ToolsPanel aus Default-Layout) + rechtes Panel volle Höhe; Objektinfo unter Attribute mergen. 4. **Modulare Custom-Bar**: Item-Picker + persistierte Custom-Bar-Ansicht. diff --git a/src/App.tsx b/src/App.tsx index 3699dc0..7b370f6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -69,7 +69,7 @@ import type { Pick3dHit } from "./viewport/Wasm3DViewport"; import { ResourceManager } from "./ui/ResourceManager"; import type { ResourceManagerHandlers } from "./ui/ResourceManager"; import { HatchSwatch } from "./ui/hatchPreview"; -import { TopBar, ViewRibbonTab } from "./ui/TopBar"; +import { TopBar, ViewRibbonTab, TextGroup } from "./ui/TopBar"; import { RibbonBar } from "./ui/ribbon/RibbonBar"; import type { RibbonTabId } from "./ui/ribbon/ribbonItems"; import { Dropdown } from "./ui/Dropdown"; @@ -3219,7 +3219,6 @@ export default function App() { onApply={(l) => setLayout(reconcileLayout(l))} /> } - textTarget={textTarget} /> {/* Ribbon-Oberleiste (2D · 3D · BIM · Ansichten). Der „Ansichten"-Tab trägt @@ -3232,7 +3231,13 @@ export default function App() { toolsEnabled={activeLevel.kind === "floor"} onSelectTool={onSelectTool} onRunCommand={onRunCommand} - viewsContent={ + tabContent={{ + text: ( +
+ +
+ ), + views: ( - } + ), + }} />
diff --git a/src/i18n/de.ts b/src/i18n/de.ts index a57b977..218d626 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -124,6 +124,7 @@ export const de = { "ribbon.tab.2d": "2D", "ribbon.tab.3d": "3D", "ribbon.tab.bim": "BIM", + "ribbon.tab.text": "Text", "ribbon.tab.views": "Ansichten", "ribbon.group.draw": "Zeichnen", "ribbon.group.modify": "Ändern", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 58e2540..7936f2c 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -123,6 +123,7 @@ export const en: Record = { "ribbon.tab.2d": "2D", "ribbon.tab.3d": "3D", "ribbon.tab.bim": "BIM", + "ribbon.tab.text": "Text", "ribbon.tab.views": "Views", "ribbon.group.draw": "Draw", "ribbon.group.modify": "Modify", diff --git a/src/styles.css b/src/styles.css index d8418a5..91dc6b5 100644 --- a/src/styles.css +++ b/src/styles.css @@ -158,8 +158,8 @@ body { display: flex; align-items: center; gap: 10px; - padding: 6px 14px; - height: 60px; + padding: 0 14px; + height: 40px; border-bottom: 1px solid var(--line); /* Oberleiste auf Ebene 1 (angehoben über dem App-Grund) + weicher Schatten nach unten → trennt sich klar von der Zeichenfläche (Tiefe). */ diff --git a/src/ui/TopBar.tsx b/src/ui/TopBar.tsx index 6c34c24..a71e0a7 100644 --- a/src/ui/TopBar.tsx +++ b/src/ui/TopBar.tsx @@ -122,14 +122,8 @@ export interface TopBarProps { /** Projekt aus einer Datei laden. */ onOpenProject: () => void; - // ── Text-Gruppe (Stempel-/Text-Formatierung) ─────────────────────────────── - /** - * Ziel der Text-Formatierung: `null` = nichts Text-artiges selektiert (die - * Controls setzen nur Defaults für neuen Text). Sonst das aktuell selektierte - * Rich-Text-Dokument (z. B. Raumstempel) samt optionalem Bereich und einem - * Setter. Ist `range` `null`, wirken Aktionen auf das GANZE Doc. - */ - textTarget: TextTarget | null; + // Die Text-/Font-Formatierung (TextGroup) liegt jetzt im „Text"-Ribbon-Tab + // (App reicht sie via RibbonBar.tabContent), nicht mehr in dieser Leiste. } /** @@ -507,7 +501,7 @@ const TEXT_SIZES: number[] = [8, 9, 10, 11, 12, 14, 18, 24, 36, 48]; * `textTarget` gesetzt, tragen die Zeile-2-Pillen einen Akzent-Rand und alle * Aktionen wirken auf `textTarget.doc` via `textTarget.apply`. */ -function TextGroup({ +export function TextGroup({ textTarget, }: { textTarget: TextTarget | null; @@ -1218,7 +1212,6 @@ export function TopBar({ onImport, onSaveProject, onOpenProject, - textTarget, }: TopBarProps) { // Dezente Scrollleiste: die Oberleiste scrollt horizontal (overflow-x). Statt // der prägnanten Standard-Leiste blenden wir eine eigene, sehr ruhige Leiste @@ -1268,19 +1261,12 @@ export function TopBar({ - {/* Ribbon-Tab-Reiter (2D/3D/BIM/Ansichten) — sitzen in dieser Zeile, der - Ribbon-Inhalt folgt darunter (RibbonBar). So gibt es nur ZWEI Bänder: - diese Leiste (Chrome + Tabs) und den Inhalt. */} + {/* Ribbon-Tab-Reiter (2D/3D/BIM/Text/Ansichten) — sitzen in dieser Zeile, + der Ribbon-Inhalt folgt darunter (RibbonBar). So gibt es nur ZWEI + Bänder: diese schmale Leiste (Chrome + Tabs) und den Inhalt. Die Text-/ + Font-Formatierung liegt jetzt im „Text"-Tab (nicht mehr hier). */} - - - {/* Text-Gruppe (kontextabhängig) — Stil/Schrift/Grösse + B/I/U · L/C/R + - Zeilenhöhe. Formatiert das aktuelle Text-Ziel (Raumstempel) live. Bleibt - in der schmalen globalen Leiste, weil sie eine kontextuelle Formatier- - Steuerung ist (kein Ansichts-/Ausgabe-Element). */} - - {/* Rechte Gruppe: Layout-Menü · Projektname · Datei-Burger (ganz rechts, GTK-Stil) — Ressourcen sitzt jetzt bei der Marke (s. o.). */}
diff --git a/src/ui/ribbon/RibbonBar.tsx b/src/ui/ribbon/RibbonBar.tsx index c1acd79..89bb242 100644 --- a/src/ui/ribbon/RibbonBar.tsx +++ b/src/ui/ribbon/RibbonBar.tsx @@ -35,11 +35,12 @@ export interface RibbonBarProps { /** Engine-Befehl starten (Ändern-Gruppe u. a.). */ onRunCommand: (name: string) => void; /** - * Reicher Inhalt des „Ansichten"-Tabs (aus der TopBar gemergte Ansichts-/Zoom-/ - * Darstellungs-Steuerung, `ViewRibbonTab`). Von App geliefert, damit dessen - * Setter/State dort bleiben. Fehlt er, zeigt der Tab den Leer-Hinweis. + * Reicher Custom-Inhalt je Tab (statt datengetriebener Item-Gruppen) — z. B. + * „Ansichten" (`ViewRibbonTab`) und „Text" (`TextGroup`). Von App geliefert, + * damit deren Setter/State dort bleiben. Ein Tab ohne Custom-Inhalt und ohne + * Gruppen zeigt den Leer-Hinweis. */ - viewsContent?: ReactNode; + tabContent?: Partial>; } /** Aktivierungs-/Highlight-Kontext, den jeder Button-Typ braucht. */ @@ -121,6 +122,7 @@ export function RibbonTabs({ export function RibbonBar(props: RibbonBarProps) { const current = RIBBON_TABS.find((tt) => tt.id === props.tab) ?? RIBBON_TABS[0]; + const custom = props.tabContent?.[current.id]; const ctx: ButtonCtx = { activeTool: props.activeTool, activeCommand: props.activeCommand, @@ -131,11 +133,11 @@ export function RibbonBar(props: RibbonBarProps) { return (
- {/* Inhalt des aktiven Tabs. Der „Ansichten"-Tab trägt reichen Custom-Inhalt - (ViewRibbonTab) statt der datengetriebenen Item-Gruppen. */} + {/* Inhalt des aktiven Tabs. Tabs mit reichem Custom-Inhalt („Ansichten", + „Text") rendern diesen statt der datengetriebenen Item-Gruppen. */}
- {current.id === "views" && props.viewsContent ? ( - props.viewsContent + {custom ? ( + custom ) : current.groups.length === 0 ? (
{t("ribbon.empty")}
) : ( diff --git a/src/ui/ribbon/ribbonItems.ts b/src/ui/ribbon/ribbonItems.ts index 7a37df0..50c8b5e 100644 --- a/src/ui/ribbon/ribbonItems.ts +++ b/src/ui/ribbon/ribbonItems.ts @@ -28,7 +28,7 @@ export interface RibbonGroup { } /** Kennung eines Ribbon-Tabs (fest, für Tab-State + i18n). */ -export type RibbonTabId = "2d" | "3d" | "bim" | "views"; +export type RibbonTabId = "2d" | "3d" | "bim" | "text" | "views"; /** Ein Ribbon-Tab: Beschriftung + gruppierte Elemente. */ export interface RibbonTab { @@ -97,6 +97,12 @@ export const RIBBON_TABS: RibbonTab[] = [ }, ], }, + { + // Custom-Inhalt: die Text-/Font-Formatierung (TextGroup), via tabContent. + id: "text", + labelKey: "ribbon.tab.text", + groups: [], + }, { id: "views", labelKey: "ribbon.tab.views",