@algenium/blocks
v1.0.1
Published
Shared UI components for Algenium applications
Readme
@algenium/blocks
Shared UI components for Algenium applications
A collection of reusable, accessible UI components built with React, Radix UI, and Tailwind CSS. Designed for use across all Algenium applications.
Installation
npm install @algenium/blocks
# or
pnpm add @algenium/blocksPeer Dependencies
This package requires the following peer dependencies:
pnpm add react react-dom next-themes lucide-react motionConfiguration
Tailwind CSS
Configure your Tailwind CSS to include this package:
// tailwind.config.js
module.exports = {
content: [
// ... your other content paths
"./node_modules/@algenium/blocks/dist/**/*.js",
],
// ... rest of your config
};Components
ThemeSwitcher
A theme switcher component that integrates with next-themes. Supports light, dark, and system themes.
Features:
- Multiple variants (default, mini)
- Multiple sizes (sm, md, lg)
- Multiple shapes (rounded, pill)
- Internationalization support
Usage:
import { ThemeSwitcher } from "@algenium/blocks";
// Default usage
<ThemeSwitcher />;
// With custom labels (for i18n)
<ThemeSwitcher
labels={{
theme: "Theme",
system: "System",
light: "Light",
dark: "Dark",
}}
/>;
// Mini variant (icon only with dropdown)
<ThemeSwitcher variant="mini" />;
// Different sizes and shapes
<ThemeSwitcher size="sm" shape="pill" />;Props:
interface ThemeSwitcherProps {
variant?: "default" | "mini";
size?: "sm" | "md" | "lg";
shape?: "rounded" | "pill";
showIcon?: boolean;
labels?: ThemeSwitcherLabels;
className?: string;
}LanguageSwitcher
A language switcher component with support for multiple languages and display variants.
Features:
- Multiple variants (default, mini)
- Multiple sizes (sm, md, lg)
- Dropdown positioning control
- Language flag/icon support
- Internationalization support
Usage:
import { LanguageSwitcher } from "@algenium/blocks";
const languages = [
{ key: "en", label: "EN", nativeName: "English" },
{ key: "es", label: "ES", nativeName: "Español" },
];
<LanguageSwitcher
languages={languages}
currentLanguage="en"
onLanguageChange={(key) => setLanguage(key)}
labels={{ language: "Language" }}
/>;
// Mini variant
<LanguageSwitcher
variant="mini"
languages={languages}
currentLanguage="en"
onLanguageChange={(key) => setLanguage(key)}
/>;
// With icon
<LanguageSwitcher
languages={languages}
currentLanguage="en"
onLanguageChange={(key) => setLanguage(key)}
showIcon
/>;Props:
interface LanguageSwitcherProps {
languages: Language[];
currentLanguage: string;
onLanguageChange: (languageKey: string) => void;
variant?: "default" | "mini";
size?: "sm" | "md" | "lg";
dropdownAlign?: "start" | "center" | "end";
showIcon?: boolean;
labels?: LanguageSwitcherLabels;
className?: string;
}
interface Language {
key: string;
label: string;
nativeName: string;
}AvatarEditor
An interactive avatar editor with zoom, rotation, and drag controls.
Features:
- Image zoom via UI buttons and slider (0.5x - 3x)
- Image rotation in 15° increments (0° - 360°)
- Drag to reposition image
- Real-time preview with circular crop
- Customizable editor size
- Optional grid overlay for alignment
- Reset all transforms
- Touch-optimized controls for mobile
Usage:
import { AvatarEditor } from "@algenium/blocks";
<AvatarEditor
value={avatarData}
onChange={(editedImage) => {
console.log("Edited image:", editedImage);
}}
/>;
// With custom size and grid
<AvatarEditor
value={avatarData}
onChange={setAvatarData}
size={280}
showGrid={true}
/>;
// With custom output settings
<AvatarEditor
value={avatarData}
onChange={setAvatarData}
outputSize={512}
outputFormat="jpeg"
outputQuality={0.95}
/>;
// With large controls for mobile/touch
<AvatarEditor
value={avatarData}
onChange={setAvatarData}
controlSize="large"
/>;Props:
interface AvatarEditorProps {
value?: string | null;
onChange?: (dataUrl: string | null) => void;
size?: number;
showGrid?: boolean;
outputSize?: number;
outputFormat?: "png" | "jpeg" | "webp";
outputQuality?: number;
controlSize?: "default" | "large";
className?: string;
}AvatarEditorDialog
A complete avatar editing experience with preview display and edit dialog.
Features:
- Large avatar display with edit button
- Opens fullscreen drawer on mobile, modal on desktop
- Integrated AvatarEditor with all controls
- Controlled component (value/onChange)
- Optional async save handler with feedback
- Success/error feedback messages
- Customizable sizes and labels
- Accessibility optimized
Usage:
import { AvatarEditorDialog } from "@algenium/blocks";
// Basic usage with value/onChange
<AvatarEditorDialog value={avatar} onChange={setAvatar} />;
// With async save handler
<AvatarEditorDialog
value={avatar}
onChange={setAvatar}
onSave={async (dataUrl) => {
const result = await uploadAvatar(dataUrl);
return result.success;
}}
dialogTitle="Edit Profile Picture"
acceptText="Save"
cancelText="Cancel"
successMessage="Avatar saved successfully!"
errorMessage="Failed to save avatar"
/>;
// With custom sizes
<AvatarEditorDialog
value={avatar}
onChange={setAvatar}
displaySize={120}
editorSize={280}
outputSize={512}
/>;Props:
interface AvatarEditorDialogProps {
value?: string | null;
onChange?: (dataUrl: string | null) => void;
onSave?: (dataUrl: string) => Promise<boolean> | boolean;
displaySize?: number;
editorSize?: number;
outputSize?: number;
placeholder?: string;
editLabel?: string;
dialogTitle?: string;
acceptText?: string;
cancelText?: string;
successMessage?: string;
errorMessage?: string;
className?: string;
}NotificationsWidget
A compact notification bell widget with popover display, sound alerts, and animations.
Features:
- Bell icon with unread count badge
- Popover with scrollable notification list
- Multiple notification types (info, success, warning, error)
- Sound notifications (chime, bell, pop, ding)
- Pulse animations (ring, glow, bounce)
- Mark as read/dismiss actions
- Click-to-navigate support
- Customizable colors, sizes, and sounds
Usage:
import { NotificationsWidget, Notification } from "@algenium/blocks";
const notifications: Notification[] = [
{
id: "1",
title: "New message",
message: "You have a new message from John",
type: "info",
timestamp: new Date(),
read: false,
},
{
id: "2",
title: "Task completed",
message: "Your export task finished successfully",
type: "success",
timestamp: new Date(Date.now() - 1000 * 60 * 5),
read: false,
},
];
<NotificationsWidget
notifications={notifications}
onMarkAsRead={(id) => markAsRead(id)}
onMarkAllAsRead={() => markAllAsRead()}
onDismiss={(id) => dismiss(id)}
onClearAll={() => clearAll()}
onNotificationClick={(notification) => {
if (notification.href) {
router.push(notification.href);
}
}}
playSound
soundType="chime"
size="md"
dotColor="red"
pulseStyle="ring"
/>;
// Minimal usage
<NotificationsWidget
notifications={notifications}
onMarkAsRead={(id) => markAsRead(id)}
/>;
// Custom configuration
<NotificationsWidget
notifications={notifications}
onMarkAsRead={(id) => markAsRead(id)}
size="lg"
dotColor="primary"
soundType="bell"
pulseStyle="glow"
maxVisible={10}
title="Alerts"
emptyMessage="No alerts"
/>;Props:
interface NotificationsWidgetProps {
notifications: Notification[];
onMarkAsRead?: (id: string) => void;
onMarkAllAsRead?: () => void;
onDismiss?: (id: string) => void;
onClearAll?: () => void;
onNotificationClick?: (notification: Notification) => void;
size?: "sm" | "md" | "lg";
maxVisible?: number; // Default: 5
playSound?: boolean; // Default: true
soundUrl?: string; // Custom sound file URL
soundType?: "chime" | "bell" | "pop" | "ding" | "none"; // Default: "chime"
soundCooldown?: number; // Default: 2000ms
className?: string;
emptyMessage?: string; // Default: "No notifications"
title?: string; // Default: "Notifications"
dotColor?: "red" | "blue" | "green" | "amber" | "purple" | "primary"; // Default: "red"
showPulse?: boolean; // Default: true
pulseStyle?: "ring" | "glow" | "bounce" | "none"; // Default: "ring"
}
interface Notification {
id: string;
title: string;
message?: string;
type?: "info" | "success" | "warning" | "error"; // Default: "info"
timestamp: Date;
read?: boolean;
href?: string; // Optional link for click navigation
}UI Primitives
The package also exports underlying UI primitives that can be used independently:
Button
import { Button } from "@algenium/blocks";
<Button variant="default">Click me</Button>;
<Button variant="outline" size="sm">
Small
</Button>;
<Button variant="ghost" size="icon">
<Icon />
</Button>;Dialog
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
} from "@algenium/blocks";
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Title</DialogTitle>
<DialogDescription>Description</DialogDescription>
</DialogHeader>
{/* Content */}
<DialogFooter>{/* Actions */}</DialogFooter>
</DialogContent>
</Dialog>;Drawer
import {
Drawer,
DrawerTrigger,
DrawerContent,
DrawerHeader,
DrawerTitle,
} from "@algenium/blocks";
<Drawer>
<DrawerTrigger>Open</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Title</DrawerTitle>
</DrawerHeader>
{/* Content */}
</DrawerContent>
</Drawer>;DropdownMenu
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from "@algenium/blocks";
<DropdownMenu>
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Item 1</DropdownMenuItem>
<DropdownMenuItem>Item 2</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>;Popover
import { Popover, PopoverTrigger, PopoverContent } from "@algenium/blocks";
<Popover>
<PopoverTrigger>Open</PopoverTrigger>
<PopoverContent>Content here</PopoverContent>
</Popover>;ScrollArea
import { ScrollArea } from "@algenium/blocks";
<ScrollArea className="h-[200px]">{/* Scrollable content */}</ScrollArea>;Slider
import { Slider } from "@algenium/blocks";
<Slider
value={[50]}
onValueChange={(value) => setValue(value[0])}
max={100}
step={1}
/>;Toggle
import { Toggle } from "@algenium/blocks";
<Toggle pressed={isPressed} onPressedChange={setIsPressed}>
Toggle me
</Toggle>;Tooltip
import {
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipProvider,
} from "@algenium/blocks";
<TooltipProvider>
<Tooltip>
<TooltipTrigger>Hover me</TooltipTrigger>
<TooltipContent>Tooltip text</TooltipContent>
</Tooltip>
</TooltipProvider>;Utilities
cn (Class Name utility)
A utility function for conditionally merging Tailwind CSS classes.
import { cn } from "@algenium/blocks";
const className = cn("base-class", condition && "conditional-class", {
"another-class": someCondition,
});TypeScript Support
All components are fully typed with TypeScript. Import types as needed:
import type {
ThemeSwitcherProps,
LanguageSwitcherProps,
Language,
AvatarEditorProps,
AvatarEditorDialogProps,
NotificationsWidgetProps,
Notification,
NotificationType,
} from "@algenium/blocks";Best Practices
Internationalization
All text-based components accept label props for i18n:
import { useTranslation } from "your-i18n-library";
const { t } = useTranslation();
<ThemeSwitcher
labels={{
theme: t("theme"),
light: t("light"),
dark: t("dark"),
system: t("system"),
}}
/>;Theme Integration
Components use CSS variables for theming. Ensure your app provides these variables:
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--border: 214.3 31.8% 91.4%;
/* ... other variables */
}Notification Management
For NotificationsWidget, implement a notification management system:
// Example notification manager
const [notifications, setNotifications] = useState<Notification[]>([]);
const markAsRead = (id: string) => {
setNotifications((prev) =>
prev.map((n) => (n.id === id ? { ...n, read: true } : n)),
);
};
const dismiss = (id: string) => {
setNotifications((prev) => prev.filter((n) => n.id !== id));
};
const addNotification = (notification: Omit<Notification, "id">) => {
const id = crypto.randomUUID();
setNotifications((prev) => [{ ...notification, id }, ...prev]);
};Contributing
This package is maintained as part of the Algenium monorepo. See the main repository for contribution guidelines.
License
Proprietary - Algenium © 2026
Version
Current version: 1.0.0-rc.4
Support
For issues or questions, contact the Algenium development team or open an issue in the repository.
