Ribbon: Text-Formatierung in eigenen Text-Tab; TopBar-Zeile auf 40px verschlankt
This commit is contained in:
@@ -52,12 +52,15 @@ RIBBON: RibbonTab[]
|
|||||||
Kamera, Ebenen-/Zeichnungs-Kombis, Detailgrad, Massstab/Zoom, Darstellungsart)
|
Kamera, Ebenen-/Zeichnungs-Kombis, Detailgrad, Massstab/Zoom, Darstellungsart)
|
||||||
sind als `ViewRibbonTab` (in `TopBar.tsx`) in den „Ansichten"-Tab gewandert;
|
sind als `ViewRibbonTab` (in `TopBar.tsx`) in den „Ansichten"-Tab gewandert;
|
||||||
App reicht sie als `viewsContent`-Node an `RibbonBar` (wie das Layout-Menü).
|
App reicht sie als `viewsContent`-Node an `RibbonBar` (wie das Layout-Menü).
|
||||||
Die TopBar ist jetzt die **schmale globale Leiste**: Marke/Ressourcen ·
|
Die **Tab-Reiter sitzen in der TopBar-Zeile** (`RibbonTabs`, in TopBar
|
||||||
Text-Formatierung · Datei/Export/Einstellungen · Fensterknöpfe.
|
gerendert; Tab-State in App), der Ribbon-Inhalt (`RibbonBar`) folgt darunter →
|
||||||
- **Offen (visuell iterieren):** Band-Höhe/Abstände des Ansichten-Tabs; ob
|
nur ZWEI Bänder. Die TopBar ist eine **schmale Leiste** (Höhe 40px): Marke/
|
||||||
Zoom/Massstab zusätzlich als Dauer-Anzeige (Statusleiste) sichtbar sein
|
Ressourcen · Tabs · Datei/Export/Einstellungen · Fensterknöpfe. Die Text-/
|
||||||
soll; Default-Tab (aktuell 2D); Text-Gruppe evtl. später eigener
|
Font-Formatierung (`TextGroup`) liegt im eigenen **„Text"-Tab** (Reihenfolge
|
||||||
„Beschriften"-Tab; 3D-Tab noch leer (Kamera-Presets/3D-Aktionen füllen).
|
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;
|
3. **Sidebar raus** (ToolsPanel aus Default-Layout) + rechtes Panel volle Höhe;
|
||||||
Objektinfo unter Attribute mergen.
|
Objektinfo unter Attribute mergen.
|
||||||
4. **Modulare Custom-Bar**: Item-Picker + persistierte Custom-Bar-Ansicht.
|
4. **Modulare Custom-Bar**: Item-Picker + persistierte Custom-Bar-Ansicht.
|
||||||
|
|||||||
+10
-4
@@ -69,7 +69,7 @@ import type { Pick3dHit } from "./viewport/Wasm3DViewport";
|
|||||||
import { ResourceManager } from "./ui/ResourceManager";
|
import { ResourceManager } from "./ui/ResourceManager";
|
||||||
import type { ResourceManagerHandlers } from "./ui/ResourceManager";
|
import type { ResourceManagerHandlers } from "./ui/ResourceManager";
|
||||||
import { HatchSwatch } from "./ui/hatchPreview";
|
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 { RibbonBar } from "./ui/ribbon/RibbonBar";
|
||||||
import type { RibbonTabId } from "./ui/ribbon/ribbonItems";
|
import type { RibbonTabId } from "./ui/ribbon/ribbonItems";
|
||||||
import { Dropdown } from "./ui/Dropdown";
|
import { Dropdown } from "./ui/Dropdown";
|
||||||
@@ -3219,7 +3219,6 @@ export default function App() {
|
|||||||
onApply={(l) => setLayout(reconcileLayout(l))}
|
onApply={(l) => setLayout(reconcileLayout(l))}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
textTarget={textTarget}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Ribbon-Oberleiste (2D · 3D · BIM · Ansichten). Der „Ansichten"-Tab trägt
|
{/* Ribbon-Oberleiste (2D · 3D · BIM · Ansichten). Der „Ansichten"-Tab trägt
|
||||||
@@ -3232,7 +3231,13 @@ export default function App() {
|
|||||||
toolsEnabled={activeLevel.kind === "floor"}
|
toolsEnabled={activeLevel.kind === "floor"}
|
||||||
onSelectTool={onSelectTool}
|
onSelectTool={onSelectTool}
|
||||||
onRunCommand={onRunCommand}
|
onRunCommand={onRunCommand}
|
||||||
viewsContent={
|
tabContent={{
|
||||||
|
text: (
|
||||||
|
<div className="ribbon-views">
|
||||||
|
<TextGroup textTarget={textTarget} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
views: (
|
||||||
<ViewRibbonTab
|
<ViewRibbonTab
|
||||||
viewToggleEnabled={activeLevel.kind === "floor"}
|
viewToggleEnabled={activeLevel.kind === "floor"}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
@@ -3279,7 +3284,8 @@ export default function App() {
|
|||||||
deleteDrawingCombo,
|
deleteDrawingCombo,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="body">
|
<div className="body">
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ export const de = {
|
|||||||
"ribbon.tab.2d": "2D",
|
"ribbon.tab.2d": "2D",
|
||||||
"ribbon.tab.3d": "3D",
|
"ribbon.tab.3d": "3D",
|
||||||
"ribbon.tab.bim": "BIM",
|
"ribbon.tab.bim": "BIM",
|
||||||
|
"ribbon.tab.text": "Text",
|
||||||
"ribbon.tab.views": "Ansichten",
|
"ribbon.tab.views": "Ansichten",
|
||||||
"ribbon.group.draw": "Zeichnen",
|
"ribbon.group.draw": "Zeichnen",
|
||||||
"ribbon.group.modify": "Ändern",
|
"ribbon.group.modify": "Ändern",
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ export const en: Record<TranslationKey, string> = {
|
|||||||
"ribbon.tab.2d": "2D",
|
"ribbon.tab.2d": "2D",
|
||||||
"ribbon.tab.3d": "3D",
|
"ribbon.tab.3d": "3D",
|
||||||
"ribbon.tab.bim": "BIM",
|
"ribbon.tab.bim": "BIM",
|
||||||
|
"ribbon.tab.text": "Text",
|
||||||
"ribbon.tab.views": "Views",
|
"ribbon.tab.views": "Views",
|
||||||
"ribbon.group.draw": "Draw",
|
"ribbon.group.draw": "Draw",
|
||||||
"ribbon.group.modify": "Modify",
|
"ribbon.group.modify": "Modify",
|
||||||
|
|||||||
+2
-2
@@ -158,8 +158,8 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 6px 14px;
|
padding: 0 14px;
|
||||||
height: 60px;
|
height: 40px;
|
||||||
border-bottom: 1px solid var(--line);
|
border-bottom: 1px solid var(--line);
|
||||||
/* Oberleiste auf Ebene 1 (angehoben über dem App-Grund) + weicher Schatten
|
/* Oberleiste auf Ebene 1 (angehoben über dem App-Grund) + weicher Schatten
|
||||||
nach unten → trennt sich klar von der Zeichenfläche (Tiefe). */
|
nach unten → trennt sich klar von der Zeichenfläche (Tiefe). */
|
||||||
|
|||||||
+7
-21
@@ -122,14 +122,8 @@ export interface TopBarProps {
|
|||||||
/** Projekt aus einer Datei laden. */
|
/** Projekt aus einer Datei laden. */
|
||||||
onOpenProject: () => void;
|
onOpenProject: () => void;
|
||||||
|
|
||||||
// ── Text-Gruppe (Stempel-/Text-Formatierung) ───────────────────────────────
|
// Die Text-/Font-Formatierung (TextGroup) liegt jetzt im „Text"-Ribbon-Tab
|
||||||
/**
|
// (App reicht sie via RibbonBar.tabContent), nicht mehr in dieser Leiste.
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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
|
* `textTarget` gesetzt, tragen die Zeile-2-Pillen einen Akzent-Rand und alle
|
||||||
* Aktionen wirken auf `textTarget.doc` via `textTarget.apply`.
|
* Aktionen wirken auf `textTarget.doc` via `textTarget.apply`.
|
||||||
*/
|
*/
|
||||||
function TextGroup({
|
export function TextGroup({
|
||||||
textTarget,
|
textTarget,
|
||||||
}: {
|
}: {
|
||||||
textTarget: TextTarget | null;
|
textTarget: TextTarget | null;
|
||||||
@@ -1218,7 +1212,6 @@ export function TopBar({
|
|||||||
onImport,
|
onImport,
|
||||||
onSaveProject,
|
onSaveProject,
|
||||||
onOpenProject,
|
onOpenProject,
|
||||||
textTarget,
|
|
||||||
}: TopBarProps) {
|
}: TopBarProps) {
|
||||||
// Dezente Scrollleiste: die Oberleiste scrollt horizontal (overflow-x). Statt
|
// Dezente Scrollleiste: die Oberleiste scrollt horizontal (overflow-x). Statt
|
||||||
// der prägnanten Standard-Leiste blenden wir eine eigene, sehr ruhige Leiste
|
// der prägnanten Standard-Leiste blenden wir eine eigene, sehr ruhige Leiste
|
||||||
@@ -1268,19 +1261,12 @@ export function TopBar({
|
|||||||
|
|
||||||
<Sep />
|
<Sep />
|
||||||
|
|
||||||
{/* Ribbon-Tab-Reiter (2D/3D/BIM/Ansichten) — sitzen in dieser Zeile, der
|
{/* Ribbon-Tab-Reiter (2D/3D/BIM/Text/Ansichten) — sitzen in dieser Zeile,
|
||||||
Ribbon-Inhalt folgt darunter (RibbonBar). So gibt es nur ZWEI Bänder:
|
der Ribbon-Inhalt folgt darunter (RibbonBar). So gibt es nur ZWEI
|
||||||
diese Leiste (Chrome + Tabs) und den Inhalt. */}
|
Bänder: diese schmale Leiste (Chrome + Tabs) und den Inhalt. Die Text-/
|
||||||
|
Font-Formatierung liegt jetzt im „Text"-Tab (nicht mehr hier). */}
|
||||||
<RibbonTabs tab={ribbonTab} onTab={onRibbonTab} />
|
<RibbonTabs tab={ribbonTab} onTab={onRibbonTab} />
|
||||||
|
|
||||||
<Sep />
|
|
||||||
|
|
||||||
{/* 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). */}
|
|
||||||
<TextGroup textTarget={textTarget} />
|
|
||||||
|
|
||||||
{/* Rechte Gruppe: Layout-Menü · Projektname · Datei-Burger (ganz rechts,
|
{/* Rechte Gruppe: Layout-Menü · Projektname · Datei-Burger (ganz rechts,
|
||||||
GTK-Stil) — Ressourcen sitzt jetzt bei der Marke (s. o.). */}
|
GTK-Stil) — Ressourcen sitzt jetzt bei der Marke (s. o.). */}
|
||||||
<div className="topbar-right">
|
<div className="topbar-right">
|
||||||
|
|||||||
@@ -35,11 +35,12 @@ export interface RibbonBarProps {
|
|||||||
/** Engine-Befehl starten (Ändern-Gruppe u. a.). */
|
/** Engine-Befehl starten (Ändern-Gruppe u. a.). */
|
||||||
onRunCommand: (name: string) => void;
|
onRunCommand: (name: string) => void;
|
||||||
/**
|
/**
|
||||||
* Reicher Inhalt des „Ansichten"-Tabs (aus der TopBar gemergte Ansichts-/Zoom-/
|
* Reicher Custom-Inhalt je Tab (statt datengetriebener Item-Gruppen) — z. B.
|
||||||
* Darstellungs-Steuerung, `ViewRibbonTab`). Von App geliefert, damit dessen
|
* „Ansichten" (`ViewRibbonTab`) und „Text" (`TextGroup`). Von App geliefert,
|
||||||
* Setter/State dort bleiben. Fehlt er, zeigt der Tab den Leer-Hinweis.
|
* damit deren Setter/State dort bleiben. Ein Tab ohne Custom-Inhalt und ohne
|
||||||
|
* Gruppen zeigt den Leer-Hinweis.
|
||||||
*/
|
*/
|
||||||
viewsContent?: ReactNode;
|
tabContent?: Partial<Record<RibbonTabId, ReactNode>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Aktivierungs-/Highlight-Kontext, den jeder Button-Typ braucht. */
|
/** Aktivierungs-/Highlight-Kontext, den jeder Button-Typ braucht. */
|
||||||
@@ -121,6 +122,7 @@ export function RibbonTabs({
|
|||||||
|
|
||||||
export function RibbonBar(props: RibbonBarProps) {
|
export function RibbonBar(props: RibbonBarProps) {
|
||||||
const current = RIBBON_TABS.find((tt) => tt.id === props.tab) ?? RIBBON_TABS[0];
|
const current = RIBBON_TABS.find((tt) => tt.id === props.tab) ?? RIBBON_TABS[0];
|
||||||
|
const custom = props.tabContent?.[current.id];
|
||||||
const ctx: ButtonCtx = {
|
const ctx: ButtonCtx = {
|
||||||
activeTool: props.activeTool,
|
activeTool: props.activeTool,
|
||||||
activeCommand: props.activeCommand,
|
activeCommand: props.activeCommand,
|
||||||
@@ -131,11 +133,11 @@ export function RibbonBar(props: RibbonBarProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ribbon">
|
<div className="ribbon">
|
||||||
{/* Inhalt des aktiven Tabs. Der „Ansichten"-Tab trägt reichen Custom-Inhalt
|
{/* Inhalt des aktiven Tabs. Tabs mit reichem Custom-Inhalt („Ansichten",
|
||||||
(ViewRibbonTab) statt der datengetriebenen Item-Gruppen. */}
|
„Text") rendern diesen statt der datengetriebenen Item-Gruppen. */}
|
||||||
<div className="ribbon-body">
|
<div className="ribbon-body">
|
||||||
{current.id === "views" && props.viewsContent ? (
|
{custom ? (
|
||||||
props.viewsContent
|
custom
|
||||||
) : current.groups.length === 0 ? (
|
) : current.groups.length === 0 ? (
|
||||||
<div className="ribbon-empty">{t("ribbon.empty")}</div>
|
<div className="ribbon-empty">{t("ribbon.empty")}</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export interface RibbonGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Kennung eines Ribbon-Tabs (fest, für Tab-State + i18n). */
|
/** 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. */
|
/** Ein Ribbon-Tab: Beschriftung + gruppierte Elemente. */
|
||||||
export interface RibbonTab {
|
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",
|
id: "views",
|
||||||
labelKey: "ribbon.tab.views",
|
labelKey: "ribbon.tab.views",
|
||||||
|
|||||||
Reference in New Issue
Block a user