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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@havena/esm-core-components

v1.0.1

Published

Core ESM components library

Readme

@havena/esm-core-components

Core ESM components library providing reusable React components, UI widgets, and utilities for building Haven ESM platform applications.

Installation

npm install @havena/esm-core-components
# or
yarn add @havena/esm-core-components

Overview

This package provides a comprehensive set of React components and utilities for building applications on the Haven ESM platform:

  • UI Components: Color scheme toggle, icons, headers, banners
  • Data Display: Data tables with sorting, filtering, and pagination
  • State Management: Loading skeletons, empty states, error states
  • Access Control: Authentication and authorization wrappers
  • Layout Components: Dashboard headers, workspace wrappers
  • Utilities: Icon picker, conditional rendering helpers

Core Features

Color Scheme Toggle

A theme toggle component that cycles through light, dark, and auto color schemes.

import { ColorSchemeToggle } from "@havena/esm-core-components";

function Header() {
  return (
    <Group>
      <ColorSchemeToggle />
    </Group>
  );
}

Features:

  • Cycles through light → dark → auto → light
  • Uses Mantine's color scheme system
  • Accessible with proper ARIA labels

Tabler Icons

Icon components and utilities for using Tabler Icons in your application.

Basic Icon Usage

import { TablerIcon } from "@havena/esm-core-components";

function MyComponent() {
  return (
    <div>
      <TablerIcon name="user" size={24} stroke={2} color="blue" />
      <TablerIcon name="settings" size={20} />
    </div>
  );
}

Icon Picker

Interactive icon picker with search and pagination:

import { TablerIconPicker } from "@havena/esm-core-components";

function IconSelector() {
  const [selectedIcon, setSelectedIcon] = useState("user");

  return (
    <TablerIconPicker
      initialIcon={selectedIcon}
      onIconSelect={(icon) => {
        setSelectedIcon(icon);
        console.log("Selected icon:", icon);
      }}
      renderTriggerComponent={({ onTrigger }) => (
        <Button onClick={onTrigger} leftSection={<TablerIcon name={selectedIcon} />}>
          Choose Icon
        </Button>
      )}
    />
  );
}

Icon Utilities:

import { getAllTablerIconNames, getTablerIconCategories } from "@havena/esm-core-components";

// Get all available icon names
const allIcons = getAllTablerIconNames();

// Get icons grouped by category
const categories = getTablerIconCategories();

Component Skeletons

Loading skeleton components for better UX during data fetching.

Table Skeleton

import { TableSkeleton } from "@havena/esm-core-components";

function LoadingTable() {
  return <TableSkeleton rows={10} columns={5} />;
}

Input Skeleton

import { InputSkeleton } from "@havena/esm-core-components";

function LoadingForm() {
  return (
    <Stack>
      <InputSkeleton />
      <InputSkeleton />
      <InputSkeleton />
    </Stack>
  );
}

State Widgets

Components for handling different application states.

Empty State

Display when there's no data to show:

import { EmptyState } from "@havena/esm-core-components";

function UserList({ users }) {
  if (users.length === 0) {
    return (
      <EmptyState
        title="No users found"
        message="Get started by creating your first user"
        icon="users"
        onAdd={() => navigate("/users/new")}
      />
    );
  }
  // ... render users
}

Error State

Display error messages with retry functionality:

import { ErrorState } from "@havena/esm-core-components";

function DataView({ error, onRetry }) {
  if (error) {
    return (
      <ErrorState
        title="Failed to load data"
        message="We encountered an error while loading the data"
        error={error}
        onRetry={onRetry}
      />
    );
  }
  // ... render data
}

When Component

Conditional rendering helper for async states:

import { When } from "@havena/esm-core-components";

function UserProfile() {
  const { data, error, isLoading } = useApi("/users/me");

  return (
    <When
      asyncState={{ data, error, isLoading }}
      loading={() => <TableSkeleton />}
      error={(err) => <ErrorState error={err} />}
      success={(user) => (
        <div>
          <h1>{user.name}</h1>
          <p>{user.email}</p>
        </div>
      )}
      elseRender={() => <EmptyState message="No user data" />}
    />
  );
}

Header Link

Navigation link component for headers and navigation menus:

import { HeaderLink } from "@havena/esm-core-components";

function Navigation() {
  return (
    <Group>
      <HeaderLink to="/dashboard" label="Dashboard" icon="dashboard" />
      <HeaderLink to="/users" label="Users" icon="users" />
      <HeaderLink
        to="/settings"
        label="Settings"
        icon="settings"
        activeWhen={(path) => path.startsWith("/settings")}
      />
    </Group>
  );
}

Drawer Mode:

function DrawerNavigation({ onClose }) {
  return (
    <Stack>
      <HeaderLink
        to="/dashboard"
        label="Dashboard"
        icon="dashboard"
        onClose={onClose}
      />
      <HeaderLink
        to="/users"
        label="Users"
        icon="users"
        onClose={onClose}
      />
    </Stack>
  );
}

Data Tables

Powerful data table component built on TanStack Table with sorting, filtering, and pagination.

Basic Data Table

import { DataTable } from "@havena/esm-core-components";
import { ColumnDef } from "@tanstack/react-table";

type User = {
  id: string;
  name: string;
  email: string;
};

const columns: ColumnDef<User>[] = [
  {
    accessorKey: "name",
    header: "Name",
  },
  {
    accessorKey: "email",
    header: "Email",
  },
];

function UserTable() {
  const users = useApi("/users");

  return (
    <DataTable
      columns={columns}
      data={users.data?.results || []}
      title="Users"
    />
  );
}

Stateful Data Table

Data table with built-in loading, error, and empty states:

import { StateFullDataTable } from "@havena/esm-core-components";

function UserTable() {
  const { data, error, isLoading } = useApi("/users");

  return (
    <StateFullDataTable
      columns={columns}
      data={data?.results || []}
      isLoading={isLoading}
      error={error}
      onAdd={() => navigate("/users/new")}
      retryOnError={() => refetch()}
      title="Users"
      nothingFoundMessage="No users found. Create your first user!"
    />
  );
}

Advanced Data Table Features

function AdvancedTable() {
  return (
    <DataTable
      columns={columns}
      data={data}
      title="Users"
      withColumnViewOptions={true}
      renderActions={(table) => (
        <Group>
          <Button onClick={() => handleBulkDelete(table)}>
            Delete Selected ({table.getFilteredSelectedRowModel().rows.length})
          </Button>
          <Button onClick={handleExport}>Export</Button>
        </Group>
      )}
      renderPaginator={(table) => (
        <Pagination
          page={table.getState().pagination.pageIndex + 1}
          total={table.getPageCount()}
          onChange={(page) => table.setPageIndex(page - 1)}
        />
      )}
      renderExpandedRow={(row) => (
        <div>
          <p>User ID: {row.original.id}</p>
          <p>Created: {row.original.createdAt}</p>
        </div>
      )}
    />
  );
}

Multiple Views

function TableWithViews() {
  return (
    <StateFullDataTable
      columns={columns}
      data={data}
      views={{
        grid: (table) => <GridView data={table.getRowModel().rows} />,
        list: (table) => <ListView data={table.getRowModel().rows} />,
      }}
      renderViewTabItem={(view) => {
        const labels = { grid: "Grid", list: "List", table: "Table" };
        return labels[view] || view;
      }}
      defaultView="table"
    />
  );
}

Data Table Sub-components:

import {
  DataTableColumnHeader,
  DataTableHeader,
  DataTableViewOptions,
  DataTablePagination,
} from "@havena/esm-core-components";

// Custom column header with sorting
const sortableColumn: ColumnDef<User> = {
  accessorKey: "name",
  header: ({ column }) => (
    <DataTableColumnHeader column={column} title="Name" />
  ),
};

// Column visibility options
<DataTableViewOptions table={table} getOptionLabel={(col) => col.id} />

// Custom pagination
<DataTablePagination table={table} />

Access Control Components

Components for handling authentication and authorization.

Authenticated Component

Wrapper component that controls rendering based on authentication state:

import { Authenticated } from "@havena/esm-core-components";

// Require authenticated user
function ProtectedPage() {
  return (
    <Authenticated
      unauthenticatedAction={{ type: "redirect", path: "/login" }}
    >
      <DashboardContent />
    </Authenticated>
  );
}

// Render function with session data
function ProfilePage() {
  return (
    <Authenticated>
      {(session, user) => (
        <div>
          <h1>Welcome, {user.name}!</h1>
          <p>Session ID: {session.id}</p>
        </div>
      )}
    </Authenticated>
  );
}

// Require guest (not logged in)
function LoginPage() {
  return (
    <Authenticated
      requireSession={false}
      unauthenticatedAction={{
        type: "redirect",
        path: "/dashboard",
      }}
    >
      <LoginForm />
    </Authenticated>
  );
}

Higher Order Component:

import { withAuthentication } from "@havena/esm-core-components";

const Dashboard = () => <div>Dashboard</div>;

const ProtectedDashboard = withAuthentication(Dashboard, {
  unauthenticatedAction: { type: "redirect", path: "/login" },
  injectSession: true, // Inject session and user as props
});

// Usage with injected props
const Profile = ({ session, user }) => (
  <div>Hello, {user.name}</div>
);

const ProfileWithAuth = withAuthentication(Profile, {
  injectSession: true,
});

Require Guest Component

Component that only renders when user is NOT authenticated:

import { RequireGuest } from "@havena/esm-core-components";

function LoginPage() {
  return (
    <RequireGuest
      callbackParamName="next"
      afterAuthRedirect="/dashboard"
    >
      <LoginForm />
    </RequireGuest>
  );
}

Higher Order Component:

import { withRequireGuest } from "@havena/esm-core-components";

const LoginPage = () => <LoginForm />;

export default withRequireGuest(LoginPage, {
  callbackParamName: "next",
  afterAuthRedirect: "/dashboard",
});

Authorization Components

Components for permission-based access control.

System-Level Authorization:

import { Authorized } from "@havena/esm-core-components";

function AdminPanel() {
  return (
    <Authorized
      level="system"
      permissions={{ user: ["impersonate", "delete"] }}
      unauthorizedAction={{ type: "hide" }}
    >
      <ImpersonateButton />
      <DeleteUserButton />
    </Authorized>
  );
}

Organization-Level Authorization:

function PropertyManagement() {
  return (
    <Authorized
      level="organization"
      permissions={{ property: ["create", "update"] }}
      unauthorizedAction={{
        type: "fallback",
        component: <Text>Upgrade to access this feature</Text>,
      }}
    >
      <CreatePropertyButton />
    </Authorized>
  );
}

Higher Order Component:

import { withAuthorization } from "@havena/esm-core-components";

const AdminDashboard = () => <div>Admin Content</div>;

const ProtectedAdminDashboard = withAuthorization(AdminDashboard, {
  level: "system",
  permissions: { user: ["list", "create", "delete"] },
  unauthorizedAction: { type: "redirect", path: "/dashboard" },
});

Organization Context

Component for handling organization context and requirements:

import { OrganizationContext } from "@havena/esm-core-components";

function OrganizationPage() {
  return (
    <OrganizationContext
      requireOrganization={true}
      noOrganizationAction={{
        type: "redirect",
        path: "/organizations/select",
      }}
    >
      {(organization) => (
        <div>
          <h1>{organization.name}</h1>
          <p>{organization.description}</p>
        </div>
      )}
    </OrganizationContext>
  );
}

Higher Order Component:

import { withOrganizationContext } from "@havena/esm-core-components";

const OrganizationSettings = ({ organization }) => (
  <div>Settings for {organization.name}</div>
);

const ProtectedSettings = withOrganizationContext(OrganizationSettings, {
  requireOrganization: true,
  injectOrganization: true,
  noOrganizationAction: {
    type: "redirect",
    path: "/organizations/select",
  },
});

Contextual Banner

A flexible banner component for displaying entity information with actions:

import { ContextualBanner } from "@havena/esm-core-components";

function PropertyBanner({ propertyId }) {
  const { data, error, isLoading } = useApi(`/properties/${propertyId}`);

  return (
    <ContextualBanner
      data={data}
      error={error}
      isLoading={isLoading}
      icon="home"
      color="blue"
      renderTitle={(property) => property.name}
      renderId={(property) => property.id}
      renderTimeStamp={(property) => formatDate(property.createdAt)}
      renderBadges={(property) => (
        <Badge color={property.status === "active" ? "green" : "gray"}>
          {property.status}
        </Badge>
      )}
      renderMenuExtensionSlot={(property) => (
        <Menu>
          <Menu.Item onClick={() => handleEdit(property)}>Edit</Menu.Item>
          <Menu.Item onClick={() => handleDelete(property)}>Delete</Menu.Item>
        </Menu>
      )}
      renderExpandedSection={(property) => (
        <Stack>
          <Text>{property.description}</Text>
          <Group>
            <Text>Address: {property.address}</Text>
            <Text>Price: {property.price}</Text>
          </Group>
        </Stack>
      )}
    />
  );
}

Dashboard Components

Dashboard Page Header

Standardized page header for dashboard pages:

import { DashboardPageHeader } from "@havena/esm-core-components";

function UsersPage() {
  return (
    <>
      <DashboardPageHeader
        title="Users"
        subTitle="Manage your organization's users"
        icon="users"
      />
      {/* Page content */}
    </>
  );
}

// With custom subtitle
function CustomPage() {
  return (
    <DashboardPageHeader
      title="Analytics"
      subTitle={() => (
        <Group>
          <Text>Last updated: {new Date().toLocaleString()}</Text>
          <Button size="xs">Refresh</Button>
        </Group>
      )}
      icon={() => <CustomIcon />}
    />
  );
}

Workspace Components

Workspace system for managing side panels and overlays.

Workspace Wrapper

import { WorkspaceWrapper } from "@havena/esm-core-components";

function UserDetailsWorkspace({ userId, onClose }) {
  const { data } = useApi(`/users/${userId}`);

  return (
    <WorkspaceWrapper
      id={`user-${userId}`}
      title="User Details"
      onClose={onClose}
      expandable={true}
    >
      <Stack p="md">
        <Text>Name: {data?.name}</Text>
        <Text>Email: {data?.email}</Text>
      </Stack>
    </WorkspaceWrapper>
  );
}

Workspace Store

import {
  useWorkspaceStore,
  useWorkspaces,
  useWorkspace,
} from "@havena/esm-core-components";

function WorkspaceManager() {
  const { workspaces, dismiss } = useWorkspaces();
  const updateWorkspaces = useWorkspaceStore((state) => state.updateWorkspaces);

  // Open a new workspace
  const openWorkspace = (id: string, title: string) => {
    const newWorkspace = {
      id,
      title,
      expanded: false,
      width: "wide",
    };
    updateWorkspaces([...workspaces, newWorkspace]);
  };

  // Get the latest workspace
  const { workspace, dismiss: dismissLatest } = useWorkspace();

  return (
    <div>
      {workspaces.map((ws) => (
        <WorkspaceWrapper
          key={ws.id}
          id={ws.id}
          title={ws.title}
          onClose={() => dismiss(ws.id)}
        >
          {/* Workspace content */}
        </WorkspaceWrapper>
      ))}
    </div>
  );
}

API Reference

UI Components

  • ColorSchemeToggle - Theme toggle button
  • TablerIcon - Icon component
  • TablerIconPicker - Interactive icon picker
  • HeaderLink - Navigation link component

Data Display

  • DataTable<TData, TValue> - Main data table component
  • StateFullDataTable<TData, TValue> - Data table with states
  • DataTableColumnHeader - Sortable column header
  • DataTableHeader - Table header with actions
  • DataTableViewOptions - Column visibility toggle
  • DataTablePagination - Pagination component

State Widgets

  • EmptyState - Empty state component
  • ErrorState - Error state component
  • When<TData, TError> - Conditional rendering helper
  • TableSkeleton - Table loading skeleton
  • InputSkeleton - Input loading skeleton

Access Control

  • Authenticated - Authentication wrapper component
  • withAuthentication - Authentication HOC
  • RequireGuest - Guest-only wrapper component
  • withRequireGuest - Guest-only HOC
  • Authorized - Permission-based wrapper component
  • withAuthorization - Authorization HOC
  • OrganizationContext - Organization context wrapper
  • withOrganizationContext - Organization context HOC

Layout Components

  • ContextualBanner<TData> - Entity information banner
  • DashboardPageHeader - Dashboard page header
  • WorkspaceWrapper - Workspace panel wrapper

Utilities

  • getAllTablerIconNames() - Get all available icon names
  • getTablerIconCategories() - Get icons grouped by category
  • useWorkspaceStore - Workspace Zustand store
  • useWorkspaces() - Get all workspaces
  • useWorkspace() - Get latest workspace

Peer Dependencies

This package requires the following peer dependencies:

  • react ^18.3.1
  • react-dom ^18.3.1
  • react-router ^5.3.4
  • react-router-dom ^5.3.4
  • @havena/esm-core-api *
  • @mantine/core ^8.3.4
  • @mantine/hooks ^8.3.4
  • @mantine/modals ^8.3.4
  • @mantine/notifications ^8.3.4
  • @mantine/spotlight ^8.3.4
  • @tabler/icons-react ^3.35.0
  • @tanstack/react-table ^8.21.3
  • zustand ^5.0.8

TypeScript Support

All components are fully typed with TypeScript. The package exports type definitions for:

  • TablerIconName - Type-safe icon names
  • TablerIconProps - Icon component props
  • DataTableProps<TData, TValue> - Data table props
  • AuthorizedProps - Authorization component props
  • AuthenticatedProps - Authentication component props
  • And more...

Best Practices

  1. Access Control: Always use server-side permission checks for security. Client-side checks are for UI optimization only.

  2. Error Handling: Use ErrorState component for consistent error display across your application.

  3. Loading States: Use skeleton components (TableSkeleton, InputSkeleton) for better perceived performance.

  4. Data Tables: Use StateFullDataTable when you need built-in loading, error, and empty states.

  5. Icons: Use TablerIcon for consistent icon rendering. Use TablerIconPicker for user-selectable icons.

  6. Workspaces: Use workspace components for side panels and overlays that need to be managed independently.

License