diff --git a/src/panels/PanelIcons.tsx b/src/panels/PanelIcons.tsx new file mode 100644 index 0000000..df61a43 --- /dev/null +++ b/src/panels/PanelIcons.tsx @@ -0,0 +1,99 @@ +// Tab-Symbole der eingebauten Dock-Panels. +// +// Gleiche Konvention wie ToolIcon (ToolsPanel.tsx) und EyeIcon (ui/EyeIcon.tsx): +// schlichte Inline-SVG-Glyphen, ~16×16, stroke=currentColor, damit das Icon die +// Textfarbe des Tabs (inaktiv/hover/aktiv) übernimmt. Keine echten Asset-Icons +// vorhanden — daher hier je Panel eine eigene kleine Glyphe. +// +// Bezeichner englisch, Kommentare deutsch (CONVENTIONS.md). + +const common = { + width: 16, + height: 16, + viewBox: "0 0 16 16", + fill: "none", + stroke: "currentColor", + strokeWidth: 1.4, + strokeLinecap: "round" as const, + strokeLinejoin: "round" as const, + "aria-hidden": true, + focusable: false, +}; + +/** Werkzeuge — Palette (2×2-Raster). */ +export function ToolsTabIcon() { + return ( + + + + + + + ); +} + +/** Attribute — Federkiel (Stift/Füllung/Strichstärke der Auswahl). */ +export function AttributesTabIcon() { + return ( + + + + + ); +} + +/** Objekt-Info — umkreistes „i". */ +export function ObjectInfoTabIcon() { + return ( + + + + + + + ); +} + +/** Zeichnungsebenen — gestapelte Geschosse (drei Bodenplatten). */ +export function DrawingLevelsTabIcon() { + return ( + + + + + + + + ); +} + +/** Ebenen — überlappende Kategorie-Blätter. */ +export function LayersTabIcon() { + return ( + + + + + + ); +} + +/** Gelände — Höhenlinien/Terrain. */ +export function SiteTabIcon() { + return ( + + + + ); +} + +/** Flächenbilanz — Balken unterschiedlicher Höhe (SIA-416-Auswertung). */ +export function RoomBalanceTabIcon() { + return ( + + + + + + ); +} diff --git a/src/panels/TabStrip.tsx b/src/panels/TabStrip.tsx index 29b290c..579a215 100644 --- a/src/panels/TabStrip.tsx +++ b/src/panels/TabStrip.tsx @@ -1,4 +1,4 @@ -// Tab-Leiste eines Docks — ein Tab je Panel-ID, Titel aus der Registry. +// Tab-Leiste eines Docks — ein Tab je Panel-ID, Icon+Titel aus der Registry. // // Rein darstellend: bekommt einen DockState (Reihenfolge der Panel-IDs + // aktiver Tab) und meldet das Greifen eines Tabs über `onStartDrag`. Der @@ -6,8 +6,12 @@ // Klick (Tab aktivieren) oder ein Ziehen (Reorder/Andocken/Lösen) wird — die // Leiste selbst kennt diese Logik nicht. // -// Titel werden über die Registry (getPanel) aufgelöst, sodass die Leiste nichts -// über konkrete Panels weiß. IDs ohne registriertes Panel (z. B. ein +// Jeder Tab zeigt nur das Panel-Icon; der Titel steht als Tooltip (title- +// Attribut) und aria-label am Button. Hat ein Panel kein Icon (z. B. ein +// Plugin ohne eigenes Symbol), fällt der Tab auf den Titeltext zurück. +// +// Icon+Titel werden über die Registry (getPanel) aufgelöst, sodass die Leiste +// nichts über konkrete Panels weiß. IDs ohne registriertes Panel (z. B. ein // abgeschaltetes Plugin) werden übersprungen — vgl. Hinweis in layout.ts. // // Datenattribute für die Drag-Trefferprüfung (panelDrag.resolveHover): @@ -64,12 +68,13 @@ export function TabStrip({ side, groupIndex, group, onStartDrag }: TabStripProps type="button" role="tab" aria-selected={active} + aria-label={title} title={title} data-tab-index={index} className={`tab${active ? " active" : ""}`} onPointerDown={(e) => onStartDrag(id, e)} > - {title} + {def.icon ?? title} ); })} diff --git a/src/panels/builtinPanels.tsx b/src/panels/builtinPanels.tsx index 617626c..5885aa4 100644 --- a/src/panels/builtinPanels.tsx +++ b/src/panels/builtinPanels.tsx @@ -22,6 +22,15 @@ import { AttributesPanel } from "./AttributesPanel"; import { ObjectInfoPanel } from "./ObjectInfoPanel"; import { SitePanel } from "./SitePanel"; import { RoomBalancePanel } from "./RoomBalancePanel"; +import { + ToolsTabIcon, + AttributesTabIcon, + ObjectInfoTabIcon, + DrawingLevelsTabIcon, + LayersTabIcon, + SiteTabIcon, + RoomBalanceTabIcon, +} from "./PanelIcons"; /** IDs der eingebauten Dock-Panels (auch für defaultLayout/Filter nutzbar). */ export const BUILTIN_PANEL_IDS = { @@ -38,6 +47,7 @@ export const BUILTIN_PANEL_IDS = { registerPanel({ id: BUILTIN_PANEL_IDS.tools, title: "nav.tools", + icon: , hasDisplayMode: false, render: () => , }); @@ -46,6 +56,7 @@ registerPanel({ registerPanel({ id: BUILTIN_PANEL_IDS.attributes, title: "nav.attributes", + icon: , hasDisplayMode: false, render: () => , }); @@ -54,6 +65,7 @@ registerPanel({ registerPanel({ id: BUILTIN_PANEL_IDS.objectInfo, title: "nav.objectInfo", + icon: , hasDisplayMode: false, render: () => , }); @@ -63,6 +75,7 @@ registerPanel({ // i18n-Key statt fertigem Text: TabStrip/PanelFrame lösen ihn über t() auf, // damit der Titel der gewählten Sprache folgt. title: "nav.drawingLevels", + icon: , hasDisplayMode: true, render: () => , }); @@ -70,6 +83,7 @@ registerPanel({ registerPanel({ id: BUILTIN_PANEL_IDS.layers, title: "nav.layers", + icon: , hasDisplayMode: true, render: () => , }); @@ -78,6 +92,7 @@ registerPanel({ registerPanel({ id: BUILTIN_PANEL_IDS.site, title: "nav.site", + icon: , hasDisplayMode: false, render: () => , }); @@ -86,6 +101,7 @@ registerPanel({ registerPanel({ id: BUILTIN_PANEL_IDS.roomBalance, title: "nav.roomBalance", + icon: , hasDisplayMode: false, render: () => , }); diff --git a/src/panels/types.ts b/src/panels/types.ts index 439ae5e..89c6d1e 100644 --- a/src/panels/types.ts +++ b/src/panels/types.ts @@ -136,6 +136,14 @@ export interface PanelDef { * unverändert zurück (Plugins können also auch einen fertigen Text setzen). */ title: string; + /** + * Symbol für den Tab in der TabStrip (der Titeltext erscheint dort nur noch + * als Tooltip). Erwartet eine kleine Inline-SVG (~16×16, stroke=currentColor), + * damit sie die Textfarbe des Tabs (inaktiv/hover/aktiv) übernimmt. Fehlt es + * (z. B. bei einem Plugin-Panel ohne eigenes Icon), fällt die TabStrip auf den + * Titeltext zurück. + */ + icon?: ReactNode; /** Rendert den Panel-Inhalt. Erhält den (generischen) PanelContext. */ render: (ctx: PanelContext) => ReactNode; /** diff --git a/src/styles.css b/src/styles.css index 115f780..0cf84b8 100644 --- a/src/styles.css +++ b/src/styles.css @@ -2402,23 +2402,24 @@ body { .tab { flex: 0 0 auto; - max-width: 160px; + width: 34px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; border: 1px solid transparent; border-bottom: none; background: transparent; color: var(--muted); font-family: inherit; - font-size: 12px; - font-weight: 600; - letter-spacing: 0.03em; - padding: 8px 14px; + padding: 0; border-radius: 8px 8px 0 0; cursor: pointer; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; transition: background 0.14s, color 0.14s, border-color 0.14s; } +.tab svg { + flex: 0 0 auto; +} .tab:hover { background: var(--accent-dim); color: var(--ink);