@mehdashti/shell
v0.2.1
Published
Application shell with navigation components and enterprise layouts for Smart Platform
Downloads
344
Maintainers
Readme
@mehdashti/shell
Enterprise application shell with navigation and layouts for Smart Platform
Installation
pnpm add @mehdashti/shell @mehdashti/ui lucide-reactFeatures
Core Shell Components
- ✅ AppShell: Complete application layout with sidebar and header
- ✅ Sidebar: Collapsible navigation with groups and nested items
- ✅ Header: User menu and actions
- ✅ Breadcrumb: Page location with overflow support (Phase 4)
- ✅ TopNav: Horizontal navigation bar (Phase 4)
- ✅ DrilldownMenu: Hierarchical drill-down navigation (Phase 4)
Enterprise Layouts (Phase 4)
- ✅ ObjectPage: SAP Fiori-style entity detail layout with tabs
- ✅ MasterDetail: Resizable two-panel layout for list-detail interfaces
- ✅ FlexibleColumn: Responsive 1-3 column layout with progressive disclosure
- ✅ Dashboard: Grid-based widget layout for dashboards
General
- ✅ Responsive: Mobile-friendly layouts
- ✅ Accessible: ARIA attributes and keyboard navigation
- ✅ Type Safe: Full TypeScript support
Quick Start
Basic Usage
import { AppShell } from "@mehdashti/shell";
import { Home, Users, Settings } from "lucide-react";
function App() {
const navigation = [
{
id: "main",
items: [
{
id: "home",
label: "Home",
icon: Home,
href: "/",
active: true,
},
{
id: "users",
label: "Users",
icon: Users,
href: "/users",
badge: 5,
},
],
},
{
id: "settings",
label: "Settings",
items: [
{
id: "settings",
label: "Settings",
icon: Settings,
href: "/settings",
},
],
},
];
const user = {
name: "John Doe",
email: "[email protected]",
};
const breadcrumbs = [
{ label: "Home", href: "/" },
{ label: "Users" },
];
return (
<AppShell
navigation={navigation}
user={user}
breadcrumbs={breadcrumbs}
onNavigate={(item) => console.log("Navigate to:", item.href)}
onLogout={() => console.log("Logout")}
>
<h1>Your Content Here</h1>
</AppShell>
);
}Components
AppShell
Main application layout combining sidebar, header, and content area.
import { AppShell } from "@mehdashti/shell";
import { Home, Settings } from "lucide-react";
<AppShell
navigation={[
{
id: "main",
items: [
{ id: "home", label: "Home", icon: Home, href: "/" },
{ id: "settings", label: "Settings", icon: Settings, href: "/settings" },
],
},
]}
user={{ name: "John Doe", email: "[email protected]" }}
breadcrumbs={[{ label: "Home", href: "/" }, { label: "Dashboard" }]}
defaultSidebarCollapsed={false}
onNavigate={(item) => console.log(item)}
onLogout={() => console.log("Logout")}
onSettings={() => console.log("Settings")}
onProfile={() => console.log("Profile")}
>
<YourContent />
</AppShell>Props:
navigation: NavigationGroup[] - Navigation groups for sidebaruser: UserInfo - User information for headerbreadcrumbs: BreadcrumbItem[] - Breadcrumb itemsdefaultSidebarCollapsed: boolean - Initial sidebar stateonNavigate: (item: NavigationItem) => void - Navigation callbackonLogout: () => void - Logout callbackonSettings: () => void - Settings callbackonProfile: () => void - Profile callbackheaderActions: ReactNode - Additional header actionschildren: ReactNode - Main content
Sidebar
Collapsible navigation sidebar with groups and nested items.
import { Sidebar } from "@mehdashti/shell";
import { Home, Users, Settings } from "lucide-react";
<Sidebar
groups={[
{
id: "main",
label: "Main Menu",
items: [
{ id: "home", label: "Home", icon: Home, href: "/", active: true },
{ id: "users", label: "Users", icon: Users, href: "/users", badge: 5 },
],
},
]}
collapsed={false}
onNavigate={(item) => console.log(item)}
/>Props:
groups: NavigationGroup[] - Navigation groupscollapsed: boolean - Collapsed stateonNavigate: (item: NavigationItem) => void - Navigation callbackclassName: string - Additional CSS class
Header
Application header with user menu and actions.
import { Header } from "@mehdashti/shell";
<Header
user={{ name: "John Doe", email: "[email protected]" }}
sidebarCollapsed={false}
onToggleSidebar={() => console.log("Toggle")}
onLogout={() => console.log("Logout")}
onSettings={() => console.log("Settings")}
onProfile={() => console.log("Profile")}
actions={<button>Custom Action</button>}
/>Props:
user: UserInfo - User informationsidebarCollapsed: boolean - Sidebar stateonToggleSidebar: () => void - Toggle callbackonLogout: () => void - Logout callbackonSettings: () => void - Settings callbackonProfile: () => void - Profile callbackactions: ReactNode - Additional actionsclassName: string - Additional CSS class
Breadcrumb
Breadcrumb navigation with overflow support for long paths (Phase 4).
import { Breadcrumb } from "@mehdashti/shell";
<Breadcrumb
items={[
{ label: "Home", href: "/" },
{ label: "Users", href: "/users" },
{ label: "John Doe", href: "/users/123" },
{ label: "Profile" },
]}
showHomeIcon={true}
maxItems={4}
/>
// With overflow - collapses middle items
<Breadcrumb
items={longBreadcrumbList}
maxItems={3}
renderCollapsed={(items) => (
<span>{items.length} more...</span>
)}
/>Props:
items: BreadcrumbItem[] - Breadcrumb itemsshowHomeIcon: boolean - Show home icon for first itemseparator: ReactNode - Custom separatormaxItems: number - Max items before collapse (Phase 4)renderCollapsed: (items) => ReactNode - Custom collapsed renderer (Phase 4)className: string - Additional CSS class
TopNav
Horizontal navigation bar for modern app layouts (Phase 4).
import { TopNav } from "@mehdashti/shell";
import { Home, Users, Settings } from "lucide-react";
<TopNav
links={[
{
id: "home",
label: "Home",
href: "/",
icon: Home,
active: true,
},
{
id: "users",
label: "Users",
href: "/users",
icon: Users,
badge: 5,
},
{
id: "settings",
label: "Settings",
href: "/settings",
icon: Settings,
disabled: true,
},
]}
onNavigate={(link) => navigate(link.href)}
/>Props:
links: TopNavLink[] - Navigation linksonNavigate: (link: TopNavLink) => void - Navigation callbackclassName: string - Additional CSS class
TopNavLink Interface:
interface TopNavLink {
id: string
label: string
href: string
icon?: React.ComponentType<{ className?: string }>
active?: boolean
disabled?: boolean
badge?: string | number
}DrilldownMenu
Hierarchical menu with drill-down navigation pattern (Phase 4).
import { DrilldownMenu } from "@mehdashti/shell";
import { Folder, File } from "lucide-react";
const menuItems = [
{
id: "docs",
label: "Documents",
icon: Folder,
children: [
{ id: "2023", label: "2023", icon: Folder, children: [...] },
{ id: "2024", label: "2024", icon: Folder, children: [...] },
],
},
{ id: "settings", label: "Settings", icon: Settings },
]
<DrilldownMenu
items={menuItems}
onSelect={(item) => console.log("Selected:", item)}
showBackButton={true}
/>Props:
items: DrilldownMenuItem[] - Menu items with optional childrenonSelect: (item: DrilldownMenuItem) => void - Selection callbackshowBackButton: boolean - Show back navigation buttonclassName: string - Additional CSS class
Enterprise Layouts
ObjectPage
SAP Fiori-style layout for entity detail pages with header, tabs, and sections (Phase 4).
import { ObjectPage } from "@mehdashti/shell";
import { User, Mail, Phone } from "lucide-react";
<ObjectPage
title="John Doe"
subtitle="Senior Developer"
avatar="/avatars/john.jpg"
headerActions={
<>
<Button>Edit</Button>
<Button variant="destructive">Delete</Button>
</>
}
tabs={[
{
id: "overview",
label: "Overview",
icon: User,
content: <div>Overview content</div>,
},
{
id: "contact",
label: "Contact",
icon: Mail,
badge: "3",
content: <div>Contact information</div>,
},
{
id: "activity",
label: "Activity",
disabled: true,
content: <div>Activity feed</div>,
},
]}
sections={[
{
id: "personal",
title: "Personal Information",
content: <div>Personal details</div>,
},
{
id: "professional",
title: "Professional Details",
collapsible: true,
defaultExpanded: false,
content: <div>Professional information</div>,
},
]}
stickyHeader={true}
/>Props:
title: string - Page titlesubtitle: string - Page subtitleavatar: string - Avatar URLheaderActions: ReactNode - Actions in headertabs: ObjectPageTab[] - Tab configurationsections: ObjectPageSection[] - Section configurationstickyHeader: boolean - Sticky header on scrollclassName: string - Additional CSS class
MasterDetail
Resizable two-panel layout for list-detail interfaces (Phase 4).
import { MasterDetail } from "@mehdashti/shell";
<MasterDetail
master={<UserList onSelectUser={setSelectedUser} />}
detail={selectedUser ? <UserDetail user={selectedUser} /> : <EmptyState />}
defaultMasterWidth={300}
minMasterWidth={200}
maxMasterWidth={500}
showResizeHandle={true}
/>Props:
master: ReactNode - Master panel content (typically a list)detail: ReactNode - Detail panel contentdefaultMasterWidth: number - Initial master panel width in pxminMasterWidth: number - Minimum master panel width in pxmaxMasterWidth: number - Maximum master panel width in pxshowResizeHandle: boolean - Show resize handle between panelsclassName: string - Additional CSS class
FlexibleColumn
Responsive 1-3 column layout with progressive disclosure (Phase 4).
import { FlexibleColumn, type ColumnLayout } from "@mehdashti/shell";
const [layout, setLayout] = useState<ColumnLayout>("TwoColumnsBegin")
<FlexibleColumn
begin={<ProductList onSelect={setProduct} />}
middle={product ? <ProductDetail product={product} /> : <EmptyState />}
end={variant ? <VariantDetail variant={variant} /> : null}
layout={layout}
onLayoutChange={setLayout}
showLayoutToggles={true}
/>Layout Modes:
OneColumn- Show only middle column (detail)TwoColumnsBegin- Show begin (33%) + middle (67%)TwoColumnsEnd- Show middle (67%) + end (33%)ThreeColumns- Show begin (25%) + middle (50%) + end (25%)
Props:
begin: ReactNode - Begin column content (typically list)middle: ReactNode - Middle column content (typically detail)end: ReactNode - End column content (typically nested detail)layout: ColumnLayout - Current layout modeonLayoutChange: (layout: ColumnLayout) => void - Layout change callbackshowLayoutToggles: boolean - Show layout toggle buttonsclassName: string - Additional CSS class
Dashboard
Grid-based layout for dashboard widgets (Phase 4).
import { Dashboard } from "@mehdashti/shell";
import { MoreVertical } from "lucide-react";
<Dashboard
widgets={[
{
id: "revenue",
title: "Total Revenue",
colSpan: 6,
rowSpan: 1,
content: <RevenueChart />,
actions: (
<Button variant="ghost" size="icon">
<MoreVertical className="size-4" />
</Button>
),
},
{
id: "users",
title: "Active Users",
colSpan: 6,
content: <UserStats />,
},
{
id: "activity",
title: "Recent Activity",
colSpan: 12,
content: <ActivityFeed />,
},
]}
columns={12}
gap="md"
/>Props:
widgets: DashboardWidget[] - Widget configurationcolumns: 4 | 6 | 12 - Number of grid columnsgap: "sm" | "md" | "lg" - Gap between widgetsclassName: string - Additional CSS class
DashboardWidget Interface:
interface DashboardWidget {
id: string
title?: string
content: React.ReactNode
colSpan?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
rowSpan?: number
actions?: React.ReactNode
className?: string
}Types
NavigationItem
interface NavigationItem {
id: string;
label: string;
icon?: LucideIcon;
href?: string;
badge?: string | number;
active?: boolean;
disabled?: boolean;
children?: NavigationItem[];
}NavigationGroup
interface NavigationGroup {
id: string;
label?: string;
items: NavigationItem[];
}BreadcrumbItem
interface BreadcrumbItem {
label: string;
href?: string;
}UserInfo
interface UserInfo {
name: string;
email?: string;
avatar?: string;
}Examples
Nested Navigation
const navigation = [
{
id: "main",
items: [
{
id: "dashboard",
label: "Dashboard",
icon: Home,
href: "/",
},
{
id: "admin",
label: "Admin",
icon: Shield,
children: [
{ id: "users", label: "Users", href: "/admin/users" },
{ id: "roles", label: "Roles", href: "/admin/roles" },
],
},
],
},
];With Badges
const navigation = [
{
id: "main",
items: [
{
id: "notifications",
label: "Notifications",
icon: Bell,
href: "/notifications",
badge: 12,
},
{
id: "messages",
label: "Messages",
icon: MessageSquare,
href: "/messages",
badge: "New",
},
],
},
];Custom Header Actions
import { Button } from "@mehdashti/ui/button";
import { Bell, Search } from "lucide-react";
<AppShell
navigation={navigation}
user={user}
headerActions={
<>
<Button variant="ghost" size="icon">
<Search className="size-5" />
</Button>
<Button variant="ghost" size="icon">
<Bell className="size-5" />
</Button>
</>
}
>
<YourContent />
</AppShell>Router Integration
// With React Router
import { useNavigate, useLocation } from "react-router-dom";
function App() {
const navigate = useNavigate();
const location = useLocation();
const navigation = [
{
id: "main",
items: [
{
id: "home",
label: "Home",
icon: Home,
href: "/",
active: location.pathname === "/",
},
{
id: "users",
label: "Users",
icon: Users,
href: "/users",
active: location.pathname.startsWith("/users"),
},
],
},
];
return (
<AppShell
navigation={navigation}
onNavigate={(item) => {
if (item.href) {
navigate(item.href);
}
}}
>
<Outlet />
</AppShell>
);
}Responsive Sidebar
import { useState, useEffect } from "react";
function App() {
const [collapsed, setCollapsed] = useState(false);
// Auto-collapse on mobile
useEffect(() => {
const handleResize = () => {
setCollapsed(window.innerWidth < 768);
};
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return (
<AppShell
navigation={navigation}
defaultSidebarCollapsed={collapsed}
>
<YourContent />
</AppShell>
);
}Styling
All components use design tokens from @mehdashti/tokens and can be customized via CSS variables:
:root {
--background: #ffffff;
--foreground: #09090b;
--border: #e4e4e7;
--accent: #f4f4f5;
--accent-foreground: #18181b;
/* ... more tokens */
}Accessibility
- All interactive elements have proper ARIA labels
- Keyboard navigation supported
- Focus visible states
- Screen reader friendly
License
MIT
