fix: mobile UI — hamburger onClick, right-tab-bar, drawer content, topbar buttons
- Topbar: hamburger button opens left drawer (was no onClick) - Topbar: project name navigates back to dashboard - Topbar: Versionen/Teilen/Hilfe/Benachrichtigungen get placeholder handlers - App.tsx: render right-tab-bar with 4 tab buttons (tool/layer/library/ki) - App.tsx: pass LeftSidebar as leftContent to MobileDrawers - App.tsx: pass RightSidebar as rightContent to MobileDrawers - MobileDrawers: accept leftContent/rightContent ReactNode props - MobileDrawers: render sidebar content in drawer bodies (was empty) - ui.types.ts: add onOpenLeftDrawer, onNavigateBack to TopbarProps - ui.types.ts: add leftContent, rightContent to MobileDrawersProps Test: 343/343 pass, tsc clean, build succeeds
This commit is contained in:
@@ -55,6 +55,13 @@ const initialKIMessages: KIMessage[] = [
|
||||
{ id: 'ki-1', role: 'assistant', content: 'Hallo! Ich bin der KI Copilot. Wie kann ich helfen?' },
|
||||
];
|
||||
|
||||
const rightTabItems: Array<{ id: DrawerTab; label: string; svg: React.ReactNode }> = [
|
||||
{ id: 'tool', label: 'Wz', svg: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg> },
|
||||
{ id: 'layer', label: 'Layer', svg: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg> },
|
||||
{ id: 'library', label: 'Lib', svg: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h7a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg> },
|
||||
{ id: 'ki', label: 'KI', svg: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/></svg> },
|
||||
];
|
||||
|
||||
const initialKISuggestions: KISuggestion[] = [
|
||||
{ id: 'sug-1', label: 'Bestuhlung automatisch generieren' },
|
||||
{ id: 'sug-2', label: 'Maße analysieren' },
|
||||
@@ -938,6 +945,8 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
onRedo={handleRedo}
|
||||
onThemeToggle={handleThemeToggle}
|
||||
theme={theme}
|
||||
onOpenLeftDrawer={() => setMobileLeftOpen(true)}
|
||||
onNavigateBack={onNavigateBack}
|
||||
/>
|
||||
<RibbonBar
|
||||
activeTab={activeRibbonTab}
|
||||
@@ -1034,6 +1043,14 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
onToggleGrid={handleToggleGrid}
|
||||
seatCount={seatCount}
|
||||
/>
|
||||
<div className="right-tab-bar">
|
||||
{rightTabItems.map(tab => (
|
||||
<button key={tab.id} className={`right-tab-btn${activeDrawerTab === tab.id ? ' active' : ''}`} onClick={() => { setActiveDrawerTab(tab.id); setMobileRightOpen(true); }}>
|
||||
{tab.svg}
|
||||
<span>{tab.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<MobileDrawers
|
||||
leftOpen={mobileLeftOpen}
|
||||
rightOpen={mobileRightOpen}
|
||||
@@ -1041,6 +1058,47 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
onCloseLeft={() => setMobileLeftOpen(false)}
|
||||
onCloseRight={() => setMobileRightOpen(false)}
|
||||
onRightTabChange={setActiveDrawerTab}
|
||||
leftContent={
|
||||
<LeftSidebar
|
||||
activeTool={activeTool}
|
||||
onToolChange={handleToolChange}
|
||||
selectedTemplate={selectedTemplate}
|
||||
onTemplateSelect={handleTemplateSelect}
|
||||
/>
|
||||
}
|
||||
rightContent={
|
||||
<RightSidebar
|
||||
activePanel={activeDrawerTab}
|
||||
onPanelChange={(p) => setActiveDrawerTab(p as DrawerTab)}
|
||||
selectedElement={selectedElement}
|
||||
layers={layers}
|
||||
blocks={blocks}
|
||||
activeLayerId={activeLayerId}
|
||||
onSelectLayer={handleSelectLayer}
|
||||
onAddLayer={handleAddLayer}
|
||||
onToggleLayer={handleToggleLayer}
|
||||
onDeleteLayer={handleDeleteLayer}
|
||||
onRenameLayer={handleRenameLayer}
|
||||
onDuplicateLayer={handleDuplicateLayer}
|
||||
onToggleLock={handleToggleLock}
|
||||
onElementsDeleted={handleElementsDeleted}
|
||||
onToggleElementVisible={handleToggleElementVisible}
|
||||
elements={elements}
|
||||
onRenameBlock={handleRenameBlock}
|
||||
onDuplicateBlock={handleDuplicateBlock}
|
||||
onDeleteBlock={handleDeleteBlock}
|
||||
onSvgImport={handleSvgImport}
|
||||
onSaveGroupAsBlock={handleSaveGroupAsBlock}
|
||||
onBlockCategoryChange={handleBlockCategoryChange}
|
||||
onBlockSearch={handleBlockSearch}
|
||||
onDragBlock={handleDragBlock}
|
||||
kiMessages={kiMessages}
|
||||
kiSuggestions={kiSuggestions}
|
||||
onKISend={handleKISend}
|
||||
onKISuggestionClick={handleSuggestionClick}
|
||||
kiLoading={kiLoading}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<BackgroundImport
|
||||
open={bgImportOpen}
|
||||
|
||||
@@ -9,7 +9,7 @@ const drawerTabs: Array<{ id: DrawerTab; label: string; svg: React.ReactNode }>
|
||||
];
|
||||
|
||||
const MobileDrawers: React.FC<MobileDrawersProps> = ({
|
||||
leftOpen, rightOpen, activeRightTab, onCloseLeft, onCloseRight, onRightTabChange,
|
||||
leftOpen, rightOpen, activeRightTab, onCloseLeft, onCloseRight, onRightTabChange, leftContent, rightContent,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@@ -23,7 +23,7 @@ const MobileDrawers: React.FC<MobileDrawersProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
<div className="drawer-body" id="drawer-left-body">
|
||||
{/* Filled by LeftSidebar content on mobile */}
|
||||
{leftContent}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -49,7 +49,7 @@ const MobileDrawers: React.FC<MobileDrawersProps> = ({
|
||||
))}
|
||||
</div>
|
||||
<div className="drawer-body" id="drawer-right-body">
|
||||
{/* Filled by panel content on mobile */}
|
||||
{rightContent}
|
||||
</div>
|
||||
</aside>
|
||||
</>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import type { TopbarProps } from '../types/ui.types';
|
||||
|
||||
const Topbar: React.FC<TopbarProps> = ({ projectName, savedStatus, onUndo, onRedo, onThemeToggle, theme, onOpenSettings }) => {
|
||||
const Topbar: React.FC<TopbarProps> = ({ projectName, savedStatus, onUndo, onRedo, onThemeToggle, theme, onOpenSettings, onOpenLeftDrawer, onNavigateBack }) => {
|
||||
return (
|
||||
<header className="topbar" role="banner">
|
||||
<div className="topbar-left">
|
||||
<button className="hamburger-btn" aria-label="Werkzeuge öffnen">
|
||||
<button className="hamburger-btn" aria-label="Werkzeuge öffnen" onClick={onOpenLeftDrawer}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
|
||||
</button>
|
||||
<div className="app-logo" aria-hidden="true">
|
||||
@@ -13,7 +13,7 @@ const Topbar: React.FC<TopbarProps> = ({ projectName, savedStatus, onUndo, onRed
|
||||
</div>
|
||||
<span className="app-name">web-cad</span>
|
||||
<span style={{ color: 'var(--color-border-strong)', margin: '0 4px' }}>|</span>
|
||||
<button className="project-name" aria-label="Projekt wechseln">
|
||||
<button className="project-name" aria-label="Projekt wechseln" onClick={onNavigateBack}>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h7a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
|
||||
<span>{projectName}</span>
|
||||
<svg className="caret" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
@@ -28,17 +28,17 @@ const Topbar: React.FC<TopbarProps> = ({ projectName, savedStatus, onUndo, onRed
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 15-6.7L21 13"/></svg>
|
||||
</button>
|
||||
<span style={{ width: '1px', height: '20px', background: 'var(--color-border)', margin: '0 4px' }}></span>
|
||||
<button className="icon-btn-top" aria-label="Versionen" title="Versionsverlauf">
|
||||
<button className="icon-btn-top" aria-label="Versionen" title="Versionsverlauf" onClick={() => console.log('Versionen clicked')}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
</button>
|
||||
<button className="icon-btn-top" aria-label="Teilen" title="Projekt teilen">
|
||||
<button className="icon-btn-top" aria-label="Teilen" title="Projekt teilen" onClick={() => console.log('Teilen clicked')}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
|
||||
</button>
|
||||
<span style={{ width: '1px', height: '20px', background: 'var(--color-border)', margin: '0 4px' }}></span>
|
||||
<button className="icon-btn-top" aria-label="Hell/Dunkel umschalten" title="Theme wechseln" onClick={onThemeToggle}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
|
||||
</button>
|
||||
<button className="icon-btn-top" aria-label="Hilfe" title="Hilfe (F1)">
|
||||
<button className="icon-btn-top" aria-label="Hilfe" title="Hilfe (F1)" onClick={() => console.log('Hilfe clicked')}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||||
</button>
|
||||
<span style={{ width: '1px', height: '20px', background: 'var(--color-border)', margin: '0 4px' }}></span>
|
||||
@@ -46,7 +46,7 @@ const Topbar: React.FC<TopbarProps> = ({ projectName, savedStatus, onUndo, onRed
|
||||
<option value="de">DE</option>
|
||||
<option value="en">EN</option>
|
||||
</select>
|
||||
<button className="icon-btn-top" aria-label="Benachrichtigungen" title="Benachrichtigungen">
|
||||
<button className="icon-btn-top" aria-label="Benachrichtigungen" title="Benachrichtigungen" onClick={() => console.log('Benachrichtigungen clicked')}>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>
|
||||
</button>
|
||||
<button className="icon-btn-top" aria-label="Einstellungen" title="Einstellungen" onClick={onOpenSettings}>
|
||||
|
||||
@@ -65,6 +65,8 @@ export interface TopbarProps {
|
||||
onThemeToggle: () => void;
|
||||
theme: Theme;
|
||||
onOpenSettings?: () => void;
|
||||
onOpenLeftDrawer?: () => void;
|
||||
onNavigateBack?: () => void;
|
||||
}
|
||||
|
||||
export interface SettingsModalProps {
|
||||
@@ -241,4 +243,6 @@ export interface MobileDrawersProps {
|
||||
onCloseLeft: () => void;
|
||||
onCloseRight: () => void;
|
||||
onRightTabChange: (tab: DrawerTab) => void;
|
||||
leftContent?: React.ReactNode;
|
||||
rightContent?: React.ReactNode;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user