npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@mehdashti/shell

v0.2.1

Published

Application shell with navigation components and enterprise layouts for Smart Platform

Downloads

344

Readme

@mehdashti/shell

Enterprise application shell with navigation and layouts for Smart Platform

Installation

pnpm add @mehdashti/shell @mehdashti/ui lucide-react

Features

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 sidebar
  • user: UserInfo - User information for header
  • breadcrumbs: BreadcrumbItem[] - Breadcrumb items
  • defaultSidebarCollapsed: boolean - Initial sidebar state
  • onNavigate: (item: NavigationItem) => void - Navigation callback
  • onLogout: () => void - Logout callback
  • onSettings: () => void - Settings callback
  • onProfile: () => void - Profile callback
  • headerActions: ReactNode - Additional header actions
  • children: 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 groups
  • collapsed: boolean - Collapsed state
  • onNavigate: (item: NavigationItem) => void - Navigation callback
  • className: 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 information
  • sidebarCollapsed: boolean - Sidebar state
  • onToggleSidebar: () => void - Toggle callback
  • onLogout: () => void - Logout callback
  • onSettings: () => void - Settings callback
  • onProfile: () => void - Profile callback
  • actions: ReactNode - Additional actions
  • className: 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 items
  • showHomeIcon: boolean - Show home icon for first item
  • separator: ReactNode - Custom separator
  • maxItems: 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 links
  • onNavigate: (link: TopNavLink) => void - Navigation callback
  • className: 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 children
  • onSelect: (item: DrilldownMenuItem) => void - Selection callback
  • showBackButton: boolean - Show back navigation button
  • className: 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 title
  • subtitle: string - Page subtitle
  • avatar: string - Avatar URL
  • headerActions: ReactNode - Actions in header
  • tabs: ObjectPageTab[] - Tab configuration
  • sections: ObjectPageSection[] - Section configuration
  • stickyHeader: boolean - Sticky header on scroll
  • className: 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 content
  • defaultMasterWidth: number - Initial master panel width in px
  • minMasterWidth: number - Minimum master panel width in px
  • maxMasterWidth: number - Maximum master panel width in px
  • showResizeHandle: boolean - Show resize handle between panels
  • className: 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 mode
  • onLayoutChange: (layout: ColumnLayout) => void - Layout change callback
  • showLayoutToggles: boolean - Show layout toggle buttons
  • className: 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 configuration
  • columns: 4 | 6 | 12 - Number of grid columns
  • gap: "sm" | "md" | "lg" - Gap between widgets
  • className: 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