11import { useState } from 'react' ;
22import { UserButton , useUser } from '@clerk/clerk-react' ;
3- import { Tabs , TabsList , TabsTrigger , TabsContent } from '@/components/ui/tabs ' ;
3+ import { Menu , X } from 'lucide-react ' ;
44import { ChangeMasterPasswordDialog } from '@/components/password/ChangeMasterPasswordDialog' ;
5+ import { Button } from '@/components/ui/button' ;
6+ import { ThemeToggle } from '@/components/ui/theme-toggle' ;
57import { hasMasterPassword } from '@/lib/encryption' ;
8+ import { APP_VERSION } from '@/constants/version' ;
69import FolderPanel from '@/components/folders' ;
710import NotesPanel from '@/components/notes/NotesPanel' ;
811import Index from '@/components/editor' ;
912import type { Note } from '@/types/note' ;
1013import type { FolderPanelProps , FilesPanelProps , EditorProps } from '@/types/layout' ;
1114
12- type MobileTab = 'folders' | 'notes' | 'editor' ;
15+ type MobileView = 'folders' | 'notes' | 'editor' ;
1316
1417interface MobileLayoutProps {
1518 selectedNote : Note | null ;
@@ -25,96 +28,169 @@ export function MobileLayout({
2528 editorProps,
2629} : MobileLayoutProps ) {
2730 const { user } = useUser ( ) ;
28- const [ activeTab , setActiveTab ] = useState < MobileTab > (
31+ const [ currentView , setCurrentView ] = useState < MobileView > (
2932 selectedNote ? 'editor' : 'folders'
3033 ) ;
34+ const [ showSidebar , setShowSidebar ] = useState ( false ) ;
3135 const [ showChangePassword , setShowChangePassword ] = useState ( false ) ;
3236
3337 const hasPassword = user ?. id ? hasMasterPassword ( user . id ) : false ;
3438
3539 // Auto-switch to editor when note is selected
3640 const handleNoteSelect = ( note : Note ) => {
3741 filesPanelProps . onSelectNote ( note ) ;
38- setActiveTab ( 'editor' ) ;
42+ setCurrentView ( 'editor' ) ;
43+ setShowSidebar ( false ) ;
44+ } ;
45+
46+ const handleViewChange = ( view : MobileView ) => {
47+ setCurrentView ( view ) ;
48+ setShowSidebar ( false ) ;
3949 } ;
4050
4151 const modifiedFilesPanelProps = {
4252 ...filesPanelProps ,
4353 onSelectNote : handleNoteSelect ,
4454 } ;
55+ const getViewTitle = ( ) => {
56+ switch ( currentView ) {
57+ case 'folders' : return 'Folders' ;
58+ case 'notes' : return 'Notes' ;
59+ case 'editor' : return selectedNote ?. title || 'Editor' ;
60+ default : return 'Typelets' ;
61+ }
62+ } ;
63+
4564 return (
4665 < >
47- < Tabs
48- value = { activeTab }
49- onValueChange = { ( value ) => {
50- if ( value === 'editor' && ! selectedNote ) return ;
51- setActiveTab ( value as MobileTab ) ;
52- } }
53- className = "flex h-screen flex-col"
54- >
55- { /* Header - only show on editor tab */ }
56- { activeTab === 'editor' && selectedNote && (
57- < div className = "border-border bg-background flex shrink-0 items-center justify-center border-b p-3" >
58- < div className = "text-foreground truncate text-sm font-medium" >
59- { selectedNote . title || 'Untitled Note' }
66+ < div className = "flex h-screen flex-col bg-background" >
67+ { /* Header */ }
68+ < div className = "border-b bg-background p-3" >
69+ < div className = "flex items-center" >
70+ < Button
71+ variant = "ghost"
72+ size = "sm"
73+ onClick = { ( ) => setShowSidebar ( true ) }
74+ className = "h-9 w-9 p-0 mr-3"
75+ >
76+ < Menu className = "h-5 w-5" />
77+ </ Button >
78+
79+ < h1 className = "text-foreground text-lg font-semibold flex-1 truncate" >
80+ { getViewTitle ( ) }
81+ </ h1 >
6082 </ div >
6183 </ div >
62- ) }
6384
64- { /* Tab Content */ }
65- < div className = "flex-1 overflow-hidden" >
66- < TabsContent value = "folders" className = "h-full m-0 p-0" >
67- < FolderPanel isOpen = { true } { ...folderPanelProps } />
68- </ TabsContent >
69-
70- < TabsContent value = "notes" className = "h-full m-0 p-0" >
71- < NotesPanel isOpen = { true } { ...modifiedFilesPanelProps } />
72- </ TabsContent >
73-
74- < TabsContent value = "editor" className = "h-full m-0 p-0" >
75- { selectedNote ? (
76- < Index { ...editorProps } />
77- ) : (
78- < div className = "flex h-full items-center justify-center text-center p-4" >
79- < div >
80- < p className = "text-muted-foreground" > Select a note to start editing</ p >
85+ { /* Content */ }
86+ < div className = "flex-1 overflow-hidden" >
87+ { currentView === 'folders' && (
88+ < FolderPanel isOpen = { true } { ...folderPanelProps } />
89+ ) }
90+
91+ { currentView === 'notes' && (
92+ < NotesPanel isOpen = { true } { ...modifiedFilesPanelProps } />
93+ ) }
94+
95+ { currentView === 'editor' && (
96+ selectedNote ? (
97+ < Index { ...editorProps } />
98+ ) : (
99+ < div className = "flex h-full items-center justify-center text-center p-4" >
100+ < div >
101+ < p className = "text-muted-foreground" > Select a note to start editing</ p >
102+ </ div >
81103 </ div >
82- </ div >
104+ )
83105 ) }
84- </ TabsContent >
106+ </ div >
85107 </ div >
86108
87- { /* Bottom Tab Bar */ }
88- < div className = "border-t bg-background p-2" >
89- < div className = "flex items-center" >
90- < TabsList className = "flex-1" >
91- < TabsTrigger value = "folders" className = "flex-1" >
92- Folders
93- </ TabsTrigger >
94- < TabsTrigger value = "notes" className = "flex-1" >
95- Notes
96- </ TabsTrigger >
97- < TabsTrigger
98- value = "editor"
99- className = "flex-1"
100- disabled = { ! selectedNote }
109+ { /* Sidebar Overlay */ }
110+ { showSidebar && (
111+ < div
112+ className = "fixed inset-0 z-50 bg-black/50"
113+ onClick = { ( ) => setShowSidebar ( false ) }
114+ />
115+ ) }
116+
117+ { /* Sidebar Drawer */ }
118+ < div
119+ className = { `fixed top-0 left-0 z-50 h-full w-80 bg-background border-r shadow-lg transition-transform duration-300 ease-in-out ${
120+ showSidebar ? 'translate-x-0' : '-translate-x-full'
121+ } `}
122+ >
123+ { /* Sidebar Header */ }
124+ < div className = "border-b p-4" >
125+ < div className = "flex items-center justify-between" >
126+ < div >
127+ < h2 className = "text-lg font-semibold" > Typelets</ h2 >
128+ < div className = "text-muted-foreground text-xs opacity-80" >
129+ v{ APP_VERSION }
130+ </ div >
131+ </ div >
132+ < Button
133+ variant = "ghost"
134+ size = "sm"
135+ onClick = { ( ) => setShowSidebar ( false ) }
136+ className = "h-8 w-8 p-0"
101137 >
102- Editor
103- </ TabsTrigger >
104- </ TabsList >
138+ < X className = "h-4 w-4" />
139+ </ Button >
140+ </ div >
141+ </ div >
142+
143+ { /* Navigation Items */ }
144+ < div className = "p-4" >
145+ < div
146+ className = { `w-full text-left p-4 rounded-lg cursor-pointer transition-colors ${
147+ currentView === 'folders'
148+ ? 'bg-primary text-primary-foreground'
149+ : 'hover:bg-accent hover:text-accent-foreground'
150+ } `}
151+ onClick = { ( ) => handleViewChange ( 'folders' ) }
152+ >
153+ < div className = "font-medium" > Folders</ div >
154+ </ div >
105155
106- < div className = "mx-3 h-9 w-px bg-border" />
156+ < div
157+ className = { `w-full text-left p-4 rounded-lg cursor-pointer transition-colors ${
158+ currentView === 'notes'
159+ ? 'bg-primary text-primary-foreground'
160+ : 'hover:bg-accent hover:text-accent-foreground'
161+ } `}
162+ onClick = { ( ) => handleViewChange ( 'notes' ) }
163+ >
164+ < div className = "font-medium" > Notes</ div >
165+ </ div >
107166
108- < div className = "flex items-center" >
109- < UserButton
110- appearance = { {
111- elements : {
112- avatarBox : 'w-8 h-8' ,
113- userButtonPopoverCard : 'w-64' ,
114- } ,
115- } }
116- afterSignOutUrl = "/"
117- >
167+ < div
168+ className = { `w-full text-left p-4 rounded-lg cursor-pointer transition-colors ${
169+ ! selectedNote
170+ ? 'opacity-50 cursor-not-allowed'
171+ : currentView === 'editor'
172+ ? 'bg-primary text-primary-foreground'
173+ : 'hover:bg-accent hover:text-accent-foreground'
174+ } `}
175+ onClick = { ( ) => selectedNote && handleViewChange ( 'editor' ) }
176+ >
177+ < div className = "font-medium" > Editor</ div >
178+ </ div >
179+ </ div >
180+
181+ { /* User Section */ }
182+ < div className = "absolute bottom-0 left-0 right-0 border-t p-4" >
183+ < div className = "flex items-center justify-between mb-4" >
184+ < div className = "flex items-center gap-3" >
185+ < UserButton
186+ appearance = { {
187+ elements : {
188+ avatarBox : 'w-10 h-10' ,
189+ userButtonPopoverCard : 'w-64' ,
190+ } ,
191+ } }
192+ afterSignOutUrl = "/"
193+ >
118194 < UserButton . MenuItems >
119195 < UserButton . Action
120196 label = "Typelets Open Source"
@@ -187,10 +263,22 @@ export function MobileLayout({
187263 ) }
188264 </ UserButton . MenuItems >
189265 </ UserButton >
266+ < div className = "min-w-0 flex-1" >
267+ < div className = "text-foreground truncate text-sm font-medium" >
268+ { user ?. fullName ??
269+ user ?. firstName ??
270+ user ?. emailAddresses [ 0 ] ?. emailAddress }
271+ </ div >
272+ < div className = "text-muted-foreground text-xs" >
273+ Tap avatar for settings
274+ </ div >
275+ </ div >
276+ </ div >
277+
278+ < ThemeToggle />
190279 </ div >
191280 </ div >
192281 </ div >
193- </ Tabs >
194282
195283 < ChangeMasterPasswordDialog
196284 open = { showChangePassword }
0 commit comments