@hamak/ui-shell
v0.5.1
Published
UI Shell Framework with theming, routing, and layout management
Maintainers
Readme
UI Shell
A comprehensive application shell framework providing theme management, feature flags, routing, and layout orchestration for microkernel-based applications.
Package Structure
This is a monorepo package containing four sub-packages following the API-SPI-IMPL pattern:
- @hamak/ui-shell-api - Type definitions, interfaces, and service tokens
- @hamak/ui-shell-spi - Service Provider Interfaces for extensibility
- @hamak/ui-shell-impl - Concrete implementations of all services
- @hamak/ui-shell-templates - React templates, components, and hooks
Features
Theme Management
- Dynamic theme switching between light, dark, and system modes
- LocalStorage persistence for user preferences
- System preference detection with automatic fallback
- Custom CSS variables support
- Event-driven theme change notifications
Feature Configuration
- Feature flags for enabling/disabling functionality
- Type-safe feature access with generic types
- Subscribe to feature changes with fine-grained listeners
- Bulk feature updates for configuration management
- Includes common feature definitions for quick setup
Router Integration
- Lazy loading support for route components
- Navigation guards for route protection
- History and hash modes for different deployment scenarios
- Route metadata for custom data attachment
- Event-driven navigation updates
Layout Management
- Slot-based layout system with priority ordering
- Responsive breakpoints (xs, sm, md, lg, xl, 2xl)
- Viewport utilities for responsive behavior
- CSS Grid layouts with pre-built templates
- Touch detection and device pixel ratio utilities
Microkernel Integration
- Plugin module for seamless microkernel integration
- Dependency injection tokens for all services
- Command registration for shell operations
- Event forwarding to microkernel hooks
- Service providers for easy consumption
Installation
# Install the packages you need
bun add @hamak/ui-shell-api @hamak/ui-shell-impl @hamak/ui-shell-templatesUsage
Basic Shell Setup
import { createShellPlugin } from '@hamak/ui-shell-impl';
const shellPlugin = createShellPlugin({
theme: {
mode: 'system', // 'light' | 'dark' | 'system'
cssVariables: {
'--primary-color': '#007bff',
},
},
features: {
'ui.sidebar': true,
'experimental.ssr': false,
},
});
// Register with your microkernel
kernel.registerPlugin(shellPlugin);Theme Management
import { SHELL_TOKEN } from '@hamak/ui-shell-api';
import type { IShell } from '@hamak/ui-shell-api';
// In your plugin or component
const shell = context.resolve<IShell>(SHELL_TOKEN);
const themeManager = shell.getThemeManager();
// Set theme
themeManager.setTheme('dark');
// Toggle theme
themeManager.toggleTheme();
// Subscribe to changes
themeManager.subscribe((theme) => {
console.log('Theme changed to:', theme);
});
// Get resolved theme (converts 'system' to actual theme)
const resolvedTheme = themeManager.getResolvedTheme(); // 'light' | 'dark'Feature Flags
const featureManager = shell.getFeatureManager();
// Check if enabled
if (featureManager.isEnabled('ui.sidebar')) {
// Show sidebar
}
// Get feature value
const pollingInterval = featureManager.get('performance.polling', 5000);
// Toggle feature
featureManager.toggle('experimental.ssr');
// Subscribe to changes
featureManager.subscribe('ui.sidebar', (enabled) => {
console.log('Sidebar:', enabled ? 'enabled' : 'disabled');
});Router Setup
import { SHELL_TOKEN } from '@hamak/ui-shell-api';
const shell = context.resolve(SHELL_TOKEN);
const router = shell.setupRouter({
mode: 'history',
routes: [
{
path: '/',
component: () => import('./pages/Home'),
},
{
path: '/about',
component: () => import('./pages/About'),
meta: { title: 'About Us' },
},
{
path: '/dashboard',
component: () => import('./pages/Dashboard'),
beforeEnter: (to, from) => {
// Route guard logic
return isAuthenticated();
},
},
],
});
// Navigate
await router.push('/about');
// Subscribe to route changes
router.subscribe((route) => {
console.log('Navigated to:', route.path);
});Layout Management
import { SHELL_TOKEN } from '@hamak/ui-shell-api';
const shell = context.resolve(SHELL_TOKEN);
const layoutManager = shell.getLayoutManager();
// Register slots
layoutManager.registerSlot({
id: 'main-nav',
area: 'header',
priority: 10,
});
// Get slots for an area
const headerSlots = layoutManager.getSlots('header');
// Responsive utilities
if (ViewportUtils.isMinBreakpoint('md')) {
// Desktop layout
} else {
// Mobile layout
}
const currentBreakpoint = ViewportUtils.getCurrentBreakpoint();Microkernel Plugin Integration
import { createShellPlugin } from '@hamak/ui-shell-impl';
import { CommonFeatures } from '@hamak/ui-shell-api';
export const shellPlugin = createShellPlugin({
theme: { mode: 'system' },
features: {
'ui.sidebar': true,
},
});
// In your plugin
export function initialize(ctx) {
// Access shell services
const shell = ctx.resolve(SHELL_TOKEN);
// Subscribe to theme changes
shell.getThemeManager().subscribe((theme) => {
console.log('Theme changed:', theme);
});
}
export function activate(ctx) {
// Access shell services
const shell = ctx.resolve(SHELL_TOKEN);
shell.getThemeManager().setTheme('dark');
}Using React Templates
import { ShellProvider, DashboardLayout } from '@hamak/ui-shell-templates';
import { useTheme, useFeatures } from '@hamak/ui-shell-templates/hooks';
// Wrap your app with ShellProvider
function App() {
return (
<ShellProvider>
<DashboardLayout />
</ShellProvider>
);
}
// Use hooks in your components
function MyComponent() {
const { theme, setTheme } = useTheme();
const { isEnabled } = useFeatures();
return (
<div>
<button onClick={() => setTheme('dark')}>Dark Mode</button>
{isEnabled('ui.sidebar') && <Sidebar />}
</div>
);
}Shell Context
import { SHELL_TOKEN } from '@hamak/ui-shell-api';
const shell = context.resolve(SHELL_TOKEN);
const shellContext = shell.getContext();
// Access current theme
console.log('Current theme:', shellContext.theme);
// Set theme
shellContext.setTheme('dark');
// Check features
if (shellContext.isFeatureEnabled('ui.sidebar')) {
// ...
}
// Responsive info
if (shellContext.viewport.isMobile) {
// Mobile UI
} else if (shellContext.viewport.isDesktop) {
// Desktop UI
}Shell Events
// Subscribe to events
shell.on('theme:changed', (event) => {
console.log('Theme changed:', event.payload);
});
shell.on('viewport:resized', (event) => {
console.log('Viewport:', event.payload.width, 'x', event.payload.height);
});
shell.on('route:changed', (event) => {
console.log('Navigated to:', event.payload.route.path);
});
// Listen to all events
shell.on('*', (event) => {
console.log('Event:', event.type, event.payload);
});API Reference
Shell
Main orchestrator for the UI shell.
Methods:
initialize()- Initialize the shellsetupRouter(options)- Setup router with routesgetRouter()- Get router instancegetThemeManager()- Get theme managergetFeatureManager()- Get feature managergetContext()- Get shell context for consumerson(type, listener)- Subscribe to eventsemit(type, payload)- Emit eventsdestroy()- Clean up resources
ThemeManager
Handles theme switching and persistence.
Methods:
getTheme()- Get current theme modegetResolvedTheme()- Get resolved theme (light/dark)setTheme(mode)- Set theme modetoggleTheme()- Toggle between light and darksubscribe(listener)- Subscribe to changessetCSSVariables(vars)- Set custom CSS variables
FeatureManager
Manages feature flags and configurations.
Methods:
isEnabled(key)- Check if feature is enabledget(key, defaultValue)- Get feature valueset(key, value)- Set feature valueenable(key)- Enable featuredisable(key)- Disable featuretoggle(key)- Toggle featuresubscribe(key, listener)- Subscribe to feature changes
Router
Handles client-side routing with lazy loading.
Methods:
push(path)- Navigate to pathreplace(path)- Replace current routeback()- Go backforward()- Go forwardgetCurrentRoute()- Get current routesubscribe(listener)- Subscribe to route changesaddGuard(guard)- Add navigation guard
LayoutManager
Manages layout slots and areas.
Methods:
registerSlot(slot)- Register a layout slotunregisterSlot(slot)- Unregister a slotgetSlots(area)- Get slots for areagetAreas()- Get all registered areashasSlots(area)- Check if area has slots
Architecture
The UI Shell is designed to integrate seamlessly with the microkernel architecture:
- Core Services: Theme, Features, Router, Layout
- Plugin Integration: Provides services via DI tokens
- Event System: Forwards events to microkernel hooks
- Commands: Registers shell commands for cross-plugin use
- Context API: Provides reactive access to shell state
Common Patterns
Responsive Layouts
import { useViewport } from '@hamak/ui-shell-templates/hooks';
// In React components
function MyComponent() {
const { isMobile, isDesktop, breakpoint } = useViewport();
return isMobile ? <MobileLayout /> : <DesktopLayout />;
}Feature Gating
import { CommonFeatures } from '@hamak/ui-shell-api';
import { useFeatures } from '@hamak/ui-shell-templates/hooks';
function MyComponent() {
const { isEnabled } = useFeatures();
return (
<div>
{isEnabled(CommonFeatures.VOICE_MODE) && <VoiceInput />}
</div>
);
}License
MIT
