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

@crystaltech/hsms-shared-ui

v0.7.12

Published

A reusable React + MUI component library for HSMS applications, including a configurable layout, theme tools, and UI primitives.

Readme

HSMS Shared UI

A reusable React + MUI component library for HSMS applications, including a configurable layout, theme tools, and UI primitives.

Installation

  • pnpm: pnpm add @crystaltech/hsms-shared-ui
  • yarn: yarn add @crystaltech/hsms-shared-ui
  • npm: npm i @crystaltech/hsms-shared-ui

Peer deps (match your app): @mui/material, @mui/icons-material, @mui/x-date-pickers, @emotion/*, react, react-dom, dayjs, react-perfect-scrollbar, simplebar-react.

Using in other modules

  • Install typings (TypeScript projects):
    • pnpm add -D typescript @types/react @types/react-dom
  • Configure TypeScript:
    • tsconfig.json:
      • compilerOptions.jsx: "react-jsx"
      • compilerOptions.moduleResolution: "bundler" (or "node")
      • compilerOptions.skipLibCheck: true (optional)
  • Import components:
    • import { MainLayout, ThemeSetting, CustomButton } from '@crystaltech/hsms-shared-ui';
  • Import types (optional):
    • import type { ITheme } from '@crystaltech/hsms-shared-ui/dist/theme/defaultTheme';
    • import type { ILayoutConfig } from '@crystaltech/hsms-shared-ui/dist/components/layout/MainLayout';
  • JavaScript projects:
    • Enable type suggestions: add // @ts-check to files or use JSDoc typedefs.
    • In VS Code ensure: javascript.suggest.autoImports and typescript.suggest.autoImports are enabled.
  • Monorepo linking:
    • Use workspace linking or pnpm link and restart the TS server if auto-import suggestions don’t show.

Quick Start (Theme)

Wrap your app with ThemeCustomization and use ThemeSetting to edit and persist the theme.

import { ThemeCustomization, ThemeSetting } from "@crystaltech/hsms-shared-ui";
import { defaultTheme, ITheme } from "@crystaltech/hsms-shared-ui/dist/theme/defaultTheme";

export default function App() {
  const [theme, setTheme] = useState<ITheme>(defaultTheme);
  const handleSave = async (next: ITheme) => { setTheme(next); return next; };
  return (
    <ThemeCustomization defaultTheme={theme}>
      <ThemeSetting theme={theme} onSave={handleSave} />
    </ThemeCustomization>
  );
}

Theme Model

  • ITheme lives in src/theme/defaultTheme.ts and includes palette (navbar, sidebar, primary/secondary, background, text, neutral) and typography.
  • Use defaultTheme as a starting point; pass a new object to re-theme.

Layout

MainLayout

  • Props: title: string, layoutConfig: ILayoutConfig, logoutHandler?: () => Promise<void>
  • ILayoutConfig:
    • userManage?: { user: UserInfo; redirectUrl: string }
    • navbar: { showHamburgerInMobile: boolean; themeToggler: boolean }
    • sideDrawer: { menuConfig: IMenuConfig[]; settingsConfig: ISettingsConfig; isMinimized: boolean; drawerWidth: number }
    • footerText: string

Example:

import { MainLayout } from "@crystaltech/hsms-shared-ui";
import { menuConfig, settingsConfig } from "@crystaltech/hsms-shared-ui/dist/components/layout/layoutConfig";

const layoutConfig = {
  navbar: { showHamburgerInMobile: true, themeToggler: false },
  sideDrawer: { menuConfig, settingsConfig, isMinimized: false, drawerWidth: 260 },
  userManage: { user, redirectUrl: "/login" },
  footerText: "© 2025 All rights reserved",
};

<MainLayout title="HSMS" layoutConfig={layoutConfig}>
  {/* children */}
</MainLayout>

MainlayoutWithWrapper

  • Wraps MainLayout with a BrowserRouter. Use when you don’t already have a router at the app root.

RoutesConfigLayout

  • Props: pageTitle, user, authenticated, userRoles?, clients?, loginMethod, logoutHandler, redirectPath?, pages, publicRoutes?, Layout?, mainLayoutConfig, NotFoundPage?.
  • Builds routes from pages and protects them via ProtectedRoute.

Example (static pages map):

import { RoutesConfigLayout } from "@crystaltech/hsms-shared-ui";
import Home from "./pages/index";
import TablePage from "./pages/table";

const pages = {
  "/src/pages/index.tsx": { default: Home },
  "/src/pages/table/index.tsx": { default: TablePage, roles: ["admin"] },
} satisfies import("@crystaltech/hsms-shared-ui/dist/routes/RoutesConfigLayout").IPages;

<RoutesConfigLayout
  pageTitle="HSMS"
  user={user}
  authenticated={isAuthenticated}
  loginMethod={login}
  logoutHandler={logout}
  redirectPath="/login"
  pages={pages}
  publicRoutes={[{ path: "/about", element: <About /> }]}
  mainLayoutConfig={layoutConfig}
/>

UserInfo supports realm_access.roles and per-client roles (resource_access[client].roles). Route-level roles override global roles if provided.

Data Table

MultiDynamicTable

  • Props: tableData: DataStructure, rowsPerPage: number, page: number, handleChangePage, handleChangeRowsPerPage.
  • Features: sticky headers, column visibility toggling, keyword filter (CustomInput), pagination.

Types (inferred):

export type FieldContentActionsProps = {
  label: string;
  icon?: React.ReactNode;
  onClick: (item: FieldItem) => void;
  variant?: "outlined" | "filled";
};
export type DatePickerProps = { date: string; onChange?: (item: FieldItem, date: Date) => void };
export type FieldItem = {
  id: number; // same across rows for a column
  fieldName: string;
  isVisible: boolean;
  fieldType: "text" | "date" | "link" | "label" | "actions" | "date-picker";
  fieldContent: string | DatePickerProps | FieldContentActionsProps[];
  to?: string; // for link
};
export type FieldItemArray = FieldItem[];
export type DataStructure = FieldItem[][]; // rows => columns

Example data:

import { MultiDynamicTable } from "@crystaltech/hsms-shared-ui";
import Visibility from "@mui/icons-material/Visibility";

const dynamicTableData: DataStructure = [
  [
    { id: 1, fieldName: "Name",    isVisible: true, fieldType: "link",  fieldContent: "Alice", to: "/users/1" },
    { id: 2, fieldName: "Status",  isVisible: true, fieldType: "label", fieldContent: "Active" },
    { id: 3, fieldName: "Joined",  isVisible: true, fieldType: "date",  fieldContent: "2025-01-01" },
    { id: 4, fieldName: "Actions", isVisible: true, fieldType: "actions", fieldContent: [
      { label: "View", icon: <Visibility />, variant: "outlined", onClick: (item) => console.log(item) },
    ] },
    { id: 5, fieldName: "Reminder", isVisible: true, fieldType: "date-picker", fieldContent: {
      date: "2025-01-05", onChange: (item, date) => console.log(item, date)
    } },
  ],
  [
    { id: 1, fieldName: "Name",    isVisible: true, fieldType: "link",  fieldContent: "Bob", to: "/users/2" },
    { id: 2, fieldName: "Status",  isVisible: true, fieldType: "label", fieldContent: "Inactive" },
    { id: 3, fieldName: "Joined",  isVisible: true, fieldType: "date",  fieldContent: "2024-12-01" },
    { id: 4, fieldName: "Actions", isVisible: true, fieldType: "actions", fieldContent: [] },
    { id: 5, fieldName: "Reminder", isVisible: true, fieldType: "date-picker", fieldContent: { date: "2025-02-01" } },
  ],
];

<MultiDynamicTable
  tableData={dynamicTableData}
  page={0}
  rowsPerPage={10}
  handleChangePage={(_, p) => setPage(p)}
  handleChangeRowsPerPage={(e) => setRowsPerPage(parseInt(e.target.value, 10))}
/>

UI Components

All components are re-exported from src/index.ts.

  • CustomButton

    • Extends MUI ButtonProps; adds startIcon, endIcon, loading.
    • loading disables the button and shows a spinner.
  • CustomIconButton

    • Thin wrapper over MUI IconButton that always renders children icon.
  • CustomInput

    • Props: startIcon?, placeholder?, onChange?(value), count?, onClear?.
    • Shows count badge and clear button when not empty.
  • CustomCheckbox

    • Props: checked?, onChange?(checked), label?. Manages internal state.
  • CustomRadioGroup

    • Props: label, options: {label,value}[], value, onChange(value).
  • CustomSelect

    • Props: label, value, options: { value: string|number; label: string }[], onChange(SelectChangeEvent).
  • CustomSwitch

    • Props: checked, onChange(checked).
  • CustomTabs

    • Props: tabs: {label,value}[], value, onChange(event,value), plus TabsProps.
  • CustomDatePicker

    • Props: label, value: Dayjs|null, onChange(Dayjs|null|undefined); clearable.
  • ControlledDatePicker

    • Props: label?, initialValue?: string, onChange?(Dayjs|null); manages its own state.
  • CustomColorPicker

    • Props: initialColor?, onChange(color); uses react-color SketchPicker.
  • CustomScrollbar

    • Props: children, maxWidth?, maxHeight?; wraps simplebar-react.
  • AsyncAutocomplete

    • Props: label, multiple?, fetchOptions(), getOptionLabel, isOptionEqualToValue, onChange?.
    • Fetches options on open; shows loading indicator.
    • Example:
      <AsyncAutocomplete
        label="Users"
        fetchOptions={async () => [{ id:1, name:"Alice" }]}
        getOptionLabel={(o) => o.name}
        isOptionEqualToValue={(o,v) => o.id === v.id}
        onChange={(sel) => console.log(sel)}
      />
  • CheckboxListWithAvatar

    • Props: items: { id:number; label:string; avatarSrc? }[], checkedItems?, onChange(checkedIds).
  • CustomSelectableList

    • Props: items: { label:string; icon? }[], onSelect?(index); supports selection and a divider after the second item.
  • Loader

    • Props: size?, text?, fullScreen?.
    • Shows animated spinner with module logo; optionally full-screen overlay.

Exports

import {
  ThemeSetting,
  ThemeCustomization,
  MainLayout,
  MainlayoutWithWrapper,
  RoutesConfigLayout,
  MultiDynamicTable,
  ProfilePages,
  // UI primitives
  AsyncAutocomplete,
  CheckboxListWithAvatar,
  ControlledDatePicker,
  CustomButton,
  CustomCheckbox,
  CustomColorPicker,
  CustomDatePicker,
  CustomIconButton,
  CustomInput,
  CustomRadioGroup,
  CustomScrollbar,
  CustomSelect,
  CustomSelectableList,
  CustomSwitch,
  CustomTabs,
  CustomTextField,
  Loader,
} from "@crystaltech/hsms-shared-ui";

Notes

  • RoutesConfigLayout uses ProtectedRoute to enforce roles and clients; route-level roles override global ones.
  • Column visibility toggling in MultiDynamicTable relies on shared id values across rows for a given column.
  • Ensure peer versions of MUI and React match your app.
  • Refer to src/theme/defaultTheme.ts for the complete ITheme shape.

Pre-Mount Splash Injector

Prevent initial white screen and show a full-screen Loader while the app initializes.

  • ESM (Vite apps): add at the very top of your entry file
    • import '@crystaltech/hsms-shared-ui/preMountSplash';
    • Signal lifecycle with events or call markAppReady() when ready.
  • IIFE (microfrontend host or legacy HTML): include the CDN script before your app bundles
    • <script src="https://unpkg.com/@crystaltech/hsms-shared-ui/dist/preMountSplash.iife.js" crossorigin="anonymous"></script>
  • Optional manual init with a custom root
    • import { initPreMountSplash } from '@crystaltech/hsms-shared-ui/preMountSplash';
    • initPreMountSplash({ rootId: 'portal-root' });
    • In IIFE hosts, signal readiness:
      <script>
        // later in bootstrap
        window.HSMSPreMountSplash?.markAppReady();
        // or: window.dispatchEvent(new Event('app-ready'))
      </script>

LocalStorage keys used

  • Theme: theme.mode, theme.themeColor
  • Branding: organization_name

PWA and CSP

  • Vite PWA: ESM import gets precached; CDN IIFE is external and fetched network-first.
  • CSP: injector inserts a small <style>; allow inline styles or configure policy accordingly.

Standard lifecycle

  • Adds app-loading class to html on init; removes it and sets app-ready on hide.
  • Sets window.__APP_READY__ = true and dispatches a global app-ready event when hidden.
  • Hides when the app signals readiness via hsms-app-mounted or app-ready.
  • You can programmatically call markAppReady() or dispatch new Event('app-ready').

Example host page: see examples/host.html.

Quick Start (Splash)

  • Choose integration:
    • Vite apps: add import '@crystaltech/hsms-shared-ui/preMountSplash' at the top of your entry file.
    • Microfrontend/legacy: include the CDN IIFE script in your host HTML.
  • Ensure branding/theme keys are available early:
    • localStorage.organization_name, localStorage.theme.
  • Optional manual init and custom root:
    • initPreMountSplash({ rootId: 'portal-root' }).
  • Signal readiness after your app mounts:
    • ESM: dispatch hsms-app-mounted and/or call markAppReady().
    • IIFE: window.HSMSPreMountSplash?.markAppReady() or window.dispatchEvent(new Event('app-ready')).
  • Verify behavior:
    • First paint: <html> has app-loading and splash is visible.
    • After readiness: splash fades out and <html> switches to app-ready.

ESM example:

import '@crystaltech/hsms-shared-ui/preMountSplash';
// ... your app bootstrap (fetch config, auth, etc.)
window.dispatchEvent(new Event('hsms-app-mounted'));
window.dispatchEvent(new Event('app-ready'));

IIFE host example:

<script src="https://unpkg.com/@crystaltech/hsms-shared-ui/dist/preMountSplash.iife.js" crossorigin="anonymous"></script>
<script>
  window.HSMSPreMountSplash?.initPreMountSplash({ rootId: 'portal-root' });
  // after app is mounted
  window.HSMSPreMountSplash?.markAppReady();
  // or: window.dispatchEvent(new Event('app-ready'));
  // optional: set branding early so it shows on splash
  localStorage.setItem('organization_name', 'HSMS');
  localStorage.setItem('theme', JSON.stringify({ mode: 'light', themeColor: '#2c5282' }));
</script>

Adoption Checklist (Microfrontends)

  • Choose one integration per app: ESM import (Vite) or IIFE CDN (host HTML) — avoid double init.
  • Standardize the mount root id (e.g., portal-root) and pass { rootId } consistently.
  • Seed brand/theme early at login/bootstrap: organization_name, theme.
  • Signal readiness on mount: call markAppReady() or dispatch new Event('app-ready').
  • CSP: allow the injected <style> in policy.
  • Performance: include the IIFE script before heavy bundles; avoid blocking inline scripts before splash.
  • PWA: ensure ESM import is precached; if using CDN IIFE, verify network fallback strategy.
  • QA: verify first paint shows splash, fade-out occurs, and <html> toggles app-loadingapp-ready.

Minimal Steps (Other Modules)

  • Vite apps (ESM):
    1. Install: pnpm add @crystaltech/hsms-shared-ui
    2. At top of src/main.tsx (or src/index.ts): import '@crystaltech/hsms-shared-ui/preMountSplash'
    3. After your app mounts: dispatch hsms-app-mounted and/or call markAppReady().
    4. Optional: set early brand/theme keys: localStorage.setItem('organization_name', 'HSMS');

React (Vite) example — recommended wiring

// src/main.tsx
import '@crystaltech/hsms-shared-ui/preMountSplash';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// src/App.tsx
import { useEffect } from 'react';
import { markAppReady } from '@crystaltech/hsms-shared-ui/preMountSplash';

export default function App() {
  useEffect(() => { markAppReady(); }, []);
  return (
    <div>Your UI</div>
  );
}
  • Microfrontend/legacy hosts (IIFE):
    1. Add script before your bundles: <script src="https://unpkg.com/@crystaltech/hsms-shared-ui/dist/preMountSplash.iife.js" crossorigin="anonymous"></script>
    2. Optional init with root id: <script>window.HSMSPreMountSplash?.initPreMountSplash({ rootId: 'portal-root' });</script>
    3. After mount, signal ready: <script>window.HSMSPreMountSplash?.markAppReady();</script>
    4. Optional: set early brand/theme keys via localStorage.