feat: Implement mobile-first navigation with bottom bar and FAB
Implemented mobile-optimized navigation structure: 1. Bottom Navigation (Mobile Only) - 5 items: Dashboard, Orders, Products, Customers, More - Fixed at bottom, always visible - Thumb-friendly positioning - Active state indication - Hidden on desktop (md:hidden) 2. More Menu Page - Overflow menu for Coupons and Settings - Clean list layout with icons - Descriptions for each item - Chevron indicators 3. FAB (Floating Action Button) - Context-aware system via FABContext - Fixed bottom-right (72px from bottom) - Hidden on desktop (md:hidden) - Ready for contextual actions per page 4. FAB Context System - Global state for FAB configuration - setFAB() / clearFAB() methods - Supports icon, label, onClick, visibility - Allows pages to control FAB behavior 5. Layout Updates - Added pb-14 to main for bottom nav spacing - BottomNav and FAB in mobile fullscreen layout - Wrapped app with FABProvider Structure (Mobile): ┌─────────────────────────────────┐ │ App Bar (will hide on scroll) │ ├─────────────────────────────────┤ │ Page Header (sticky, contextual)│ ├─────────────────────────────────┤ │ Submenu (sticky) │ ├─────────────────────────────────┤ │ Content (scrollable) │ │ [+] FAB │ ├─────────────────────────────────┤ │ Bottom Nav (fixed) │ └─────────────────────────────────┘ Next Steps: - Implement scroll-hide for app bar - Add contextual FAB per page - Test on real devices Files Created: - BottomNav.tsx: Bottom navigation component - More/index.tsx: More menu page - FABContext.tsx: FAB state management - FAB.tsx: Floating action button component - useScrollDirection.ts: Scroll detection hook Files Modified: - App.tsx: Added bottom nav, FAB, More route, providers
This commit is contained in:
43
admin-spa/src/contexts/FABContext.tsx
Normal file
43
admin-spa/src/contexts/FABContext.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
|
||||
export interface FABConfig {
|
||||
icon?: ReactNode;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
visible?: boolean;
|
||||
variant?: 'primary' | 'secondary';
|
||||
}
|
||||
|
||||
interface FABContextType {
|
||||
config: FABConfig | null;
|
||||
setFAB: (config: FABConfig | null) => void;
|
||||
clearFAB: () => void;
|
||||
}
|
||||
|
||||
const FABContext = createContext<FABContextType | undefined>(undefined);
|
||||
|
||||
export function FABProvider({ children }: { children: ReactNode }) {
|
||||
const [config, setConfig] = useState<FABConfig | null>(null);
|
||||
|
||||
const setFAB = (newConfig: FABConfig | null) => {
|
||||
setConfig(newConfig);
|
||||
};
|
||||
|
||||
const clearFAB = () => {
|
||||
setConfig(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<FABContext.Provider value={{ config, setFAB, clearFAB }}>
|
||||
{children}
|
||||
</FABContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useFAB() {
|
||||
const context = useContext(FABContext);
|
||||
if (!context) {
|
||||
throw new Error('useFAB must be used within FABProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user