@algenium/blocks 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,676 @@
1
+ # @algenium/blocks
2
+
3
+ > Shared UI components for Algenium applications
4
+
5
+ A collection of reusable, accessible UI components built with React, Radix UI, and Tailwind CSS. Designed for use across all Algenium applications.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @algenium/blocks
11
+ # or
12
+ pnpm add @algenium/blocks
13
+ ```
14
+
15
+ ## Peer Dependencies
16
+
17
+ This package requires the following peer dependencies:
18
+
19
+ ```bash
20
+ pnpm add react react-dom next-themes lucide-react motion
21
+ ```
22
+
23
+ ## Configuration
24
+
25
+ ### Tailwind CSS
26
+
27
+ Configure your Tailwind CSS to include this package:
28
+
29
+ ```js
30
+ // tailwind.config.js
31
+ module.exports = {
32
+ content: [
33
+ // ... your other content paths
34
+ "./node_modules/@algenium/blocks/dist/**/*.js",
35
+ ],
36
+ // ... rest of your config
37
+ };
38
+ ```
39
+
40
+ ## Components
41
+
42
+ ### ThemeSwitcher
43
+
44
+ A theme switcher component that integrates with `next-themes`. Supports light, dark, and system themes.
45
+
46
+ **Features:**
47
+
48
+ - Multiple variants (default, mini)
49
+ - Multiple sizes (sm, md, lg)
50
+ - Multiple shapes (rounded, pill)
51
+ - Internationalization support
52
+
53
+ **Usage:**
54
+
55
+ ```tsx
56
+ import { ThemeSwitcher } from "@algenium/blocks";
57
+
58
+ // Default usage
59
+ <ThemeSwitcher />;
60
+
61
+ // With custom labels (for i18n)
62
+ <ThemeSwitcher
63
+ labels={{
64
+ theme: "Theme",
65
+ system: "System",
66
+ light: "Light",
67
+ dark: "Dark",
68
+ }}
69
+ />;
70
+
71
+ // Mini variant (icon only with dropdown)
72
+ <ThemeSwitcher variant="mini" />;
73
+
74
+ // Different sizes and shapes
75
+ <ThemeSwitcher size="sm" shape="pill" />;
76
+ ```
77
+
78
+ **Props:**
79
+
80
+ ```typescript
81
+ interface ThemeSwitcherProps {
82
+ variant?: "default" | "mini";
83
+ size?: "sm" | "md" | "lg";
84
+ shape?: "rounded" | "pill";
85
+ showIcon?: boolean;
86
+ labels?: ThemeSwitcherLabels;
87
+ className?: string;
88
+ }
89
+ ```
90
+
91
+ ---
92
+
93
+ ### LanguageSwitcher
94
+
95
+ A language switcher component with support for multiple languages and display variants.
96
+
97
+ **Features:**
98
+
99
+ - Multiple variants (default, mini)
100
+ - Multiple sizes (sm, md, lg)
101
+ - Dropdown positioning control
102
+ - Language flag/icon support
103
+ - Internationalization support
104
+
105
+ **Usage:**
106
+
107
+ ```tsx
108
+ import { LanguageSwitcher } from "@algenium/blocks";
109
+
110
+ const languages = [
111
+ { key: "en", label: "EN", nativeName: "English" },
112
+ { key: "es", label: "ES", nativeName: "Español" },
113
+ ];
114
+
115
+ <LanguageSwitcher
116
+ languages={languages}
117
+ currentLanguage="en"
118
+ onLanguageChange={(key) => setLanguage(key)}
119
+ labels={{ language: "Language" }}
120
+ />;
121
+
122
+ // Mini variant
123
+ <LanguageSwitcher
124
+ variant="mini"
125
+ languages={languages}
126
+ currentLanguage="en"
127
+ onLanguageChange={(key) => setLanguage(key)}
128
+ />;
129
+
130
+ // With icon
131
+ <LanguageSwitcher
132
+ languages={languages}
133
+ currentLanguage="en"
134
+ onLanguageChange={(key) => setLanguage(key)}
135
+ showIcon
136
+ />;
137
+ ```
138
+
139
+ **Props:**
140
+
141
+ ```typescript
142
+ interface LanguageSwitcherProps {
143
+ languages: Language[];
144
+ currentLanguage: string;
145
+ onLanguageChange: (languageKey: string) => void;
146
+ variant?: "default" | "mini";
147
+ size?: "sm" | "md" | "lg";
148
+ dropdownAlign?: "start" | "center" | "end";
149
+ showIcon?: boolean;
150
+ labels?: LanguageSwitcherLabels;
151
+ className?: string;
152
+ }
153
+
154
+ interface Language {
155
+ key: string;
156
+ label: string;
157
+ nativeName: string;
158
+ }
159
+ ```
160
+
161
+ ---
162
+
163
+ ### AvatarEditor
164
+
165
+ An interactive avatar editor with zoom, rotation, and drag controls.
166
+
167
+ **Features:**
168
+
169
+ - Image zoom via UI buttons and slider (0.5x - 3x)
170
+ - Image rotation in 15° increments (0° - 360°)
171
+ - Drag to reposition image
172
+ - Real-time preview with circular crop
173
+ - Customizable editor size
174
+ - Optional grid overlay for alignment
175
+ - Reset all transforms
176
+ - Touch-optimized controls for mobile
177
+
178
+ **Usage:**
179
+
180
+ ```tsx
181
+ import { AvatarEditor } from "@algenium/blocks";
182
+
183
+ <AvatarEditor
184
+ value={avatarData}
185
+ onChange={(editedImage) => {
186
+ console.log("Edited image:", editedImage);
187
+ }}
188
+ />;
189
+
190
+ // With custom size and grid
191
+ <AvatarEditor
192
+ value={avatarData}
193
+ onChange={setAvatarData}
194
+ size={280}
195
+ showGrid={true}
196
+ />;
197
+
198
+ // With custom output settings
199
+ <AvatarEditor
200
+ value={avatarData}
201
+ onChange={setAvatarData}
202
+ outputSize={512}
203
+ outputFormat="jpeg"
204
+ outputQuality={0.95}
205
+ />;
206
+
207
+ // With large controls for mobile/touch
208
+ <AvatarEditor
209
+ value={avatarData}
210
+ onChange={setAvatarData}
211
+ controlSize="large"
212
+ />;
213
+ ```
214
+
215
+ **Props:**
216
+
217
+ ```typescript
218
+ interface AvatarEditorProps {
219
+ value?: string | null;
220
+ onChange?: (dataUrl: string | null) => void;
221
+ size?: number;
222
+ showGrid?: boolean;
223
+ outputSize?: number;
224
+ outputFormat?: "png" | "jpeg" | "webp";
225
+ outputQuality?: number;
226
+ controlSize?: "default" | "large";
227
+ className?: string;
228
+ }
229
+ ```
230
+
231
+ ---
232
+
233
+ ### AvatarEditorDialog
234
+
235
+ A complete avatar editing experience with preview display and edit dialog.
236
+
237
+ **Features:**
238
+
239
+ - Large avatar display with edit button
240
+ - Opens fullscreen drawer on mobile, modal on desktop
241
+ - Integrated AvatarEditor with all controls
242
+ - Controlled component (value/onChange)
243
+ - Optional async save handler with feedback
244
+ - Success/error feedback messages
245
+ - Customizable sizes and labels
246
+ - Accessibility optimized
247
+
248
+ **Usage:**
249
+
250
+ ```tsx
251
+ import { AvatarEditorDialog } from "@algenium/blocks";
252
+
253
+ // Basic usage with value/onChange
254
+ <AvatarEditorDialog value={avatar} onChange={setAvatar} />;
255
+
256
+ // With async save handler
257
+ <AvatarEditorDialog
258
+ value={avatar}
259
+ onChange={setAvatar}
260
+ onSave={async (dataUrl) => {
261
+ const result = await uploadAvatar(dataUrl);
262
+ return result.success;
263
+ }}
264
+ dialogTitle="Edit Profile Picture"
265
+ acceptText="Save"
266
+ cancelText="Cancel"
267
+ successMessage="Avatar saved successfully!"
268
+ errorMessage="Failed to save avatar"
269
+ />;
270
+
271
+ // With custom sizes
272
+ <AvatarEditorDialog
273
+ value={avatar}
274
+ onChange={setAvatar}
275
+ displaySize={120}
276
+ editorSize={280}
277
+ outputSize={512}
278
+ />;
279
+ ```
280
+
281
+ **Props:**
282
+
283
+ ```typescript
284
+ interface AvatarEditorDialogProps {
285
+ value?: string | null;
286
+ onChange?: (dataUrl: string | null) => void;
287
+ onSave?: (dataUrl: string) => Promise<boolean> | boolean;
288
+ displaySize?: number;
289
+ editorSize?: number;
290
+ outputSize?: number;
291
+ placeholder?: string;
292
+ editLabel?: string;
293
+ dialogTitle?: string;
294
+ acceptText?: string;
295
+ cancelText?: string;
296
+ successMessage?: string;
297
+ errorMessage?: string;
298
+ className?: string;
299
+ }
300
+ ```
301
+
302
+ ---
303
+
304
+ ### NotificationsWidget
305
+
306
+ A compact notification bell widget with popover display, sound alerts, and animations.
307
+
308
+ **Features:**
309
+
310
+ - Bell icon with unread count badge
311
+ - Popover with scrollable notification list
312
+ - Multiple notification types (info, success, warning, error)
313
+ - Sound notifications (chime, bell, pop, ding)
314
+ - Pulse animations (ring, glow, bounce)
315
+ - Mark as read/dismiss actions
316
+ - Click-to-navigate support
317
+ - Customizable colors, sizes, and sounds
318
+
319
+ **Usage:**
320
+
321
+ ```tsx
322
+ import { NotificationsWidget, Notification } from "@algenium/blocks";
323
+
324
+ const notifications: Notification[] = [
325
+ {
326
+ id: "1",
327
+ title: "New message",
328
+ message: "You have a new message from John",
329
+ type: "info",
330
+ timestamp: new Date(),
331
+ read: false,
332
+ },
333
+ {
334
+ id: "2",
335
+ title: "Task completed",
336
+ message: "Your export task finished successfully",
337
+ type: "success",
338
+ timestamp: new Date(Date.now() - 1000 * 60 * 5),
339
+ read: false,
340
+ },
341
+ ];
342
+
343
+ <NotificationsWidget
344
+ notifications={notifications}
345
+ onMarkAsRead={(id) => markAsRead(id)}
346
+ onMarkAllAsRead={() => markAllAsRead()}
347
+ onDismiss={(id) => dismiss(id)}
348
+ onClearAll={() => clearAll()}
349
+ onNotificationClick={(notification) => {
350
+ if (notification.href) {
351
+ router.push(notification.href);
352
+ }
353
+ }}
354
+ playSound
355
+ soundType="chime"
356
+ size="md"
357
+ dotColor="red"
358
+ pulseStyle="ring"
359
+ />;
360
+
361
+ // Minimal usage
362
+ <NotificationsWidget
363
+ notifications={notifications}
364
+ onMarkAsRead={(id) => markAsRead(id)}
365
+ />;
366
+
367
+ // Custom configuration
368
+ <NotificationsWidget
369
+ notifications={notifications}
370
+ onMarkAsRead={(id) => markAsRead(id)}
371
+ size="lg"
372
+ dotColor="primary"
373
+ soundType="bell"
374
+ pulseStyle="glow"
375
+ maxVisible={10}
376
+ title="Alerts"
377
+ emptyMessage="No alerts"
378
+ />;
379
+ ```
380
+
381
+ **Props:**
382
+
383
+ ```typescript
384
+ interface NotificationsWidgetProps {
385
+ notifications: Notification[];
386
+ onMarkAsRead?: (id: string) => void;
387
+ onMarkAllAsRead?: () => void;
388
+ onDismiss?: (id: string) => void;
389
+ onClearAll?: () => void;
390
+ onNotificationClick?: (notification: Notification) => void;
391
+ size?: "sm" | "md" | "lg";
392
+ maxVisible?: number; // Default: 5
393
+ playSound?: boolean; // Default: true
394
+ soundUrl?: string; // Custom sound file URL
395
+ soundType?: "chime" | "bell" | "pop" | "ding" | "none"; // Default: "chime"
396
+ soundCooldown?: number; // Default: 2000ms
397
+ className?: string;
398
+ emptyMessage?: string; // Default: "No notifications"
399
+ title?: string; // Default: "Notifications"
400
+ dotColor?: "red" | "blue" | "green" | "amber" | "purple" | "primary"; // Default: "red"
401
+ showPulse?: boolean; // Default: true
402
+ pulseStyle?: "ring" | "glow" | "bounce" | "none"; // Default: "ring"
403
+ }
404
+
405
+ interface Notification {
406
+ id: string;
407
+ title: string;
408
+ message?: string;
409
+ type?: "info" | "success" | "warning" | "error"; // Default: "info"
410
+ timestamp: Date;
411
+ read?: boolean;
412
+ href?: string; // Optional link for click navigation
413
+ }
414
+ ```
415
+
416
+ ---
417
+
418
+ ## UI Primitives
419
+
420
+ The package also exports underlying UI primitives that can be used independently:
421
+
422
+ ### Button
423
+
424
+ ```tsx
425
+ import { Button } from "@algenium/blocks";
426
+
427
+ <Button variant="default">Click me</Button>;
428
+ <Button variant="outline" size="sm">
429
+ Small
430
+ </Button>;
431
+ <Button variant="ghost" size="icon">
432
+ <Icon />
433
+ </Button>;
434
+ ```
435
+
436
+ ### Dialog
437
+
438
+ ```tsx
439
+ import {
440
+ Dialog,
441
+ DialogTrigger,
442
+ DialogContent,
443
+ DialogHeader,
444
+ DialogTitle,
445
+ DialogDescription,
446
+ DialogFooter,
447
+ } from "@algenium/blocks";
448
+
449
+ <Dialog>
450
+ <DialogTrigger>Open</DialogTrigger>
451
+ <DialogContent>
452
+ <DialogHeader>
453
+ <DialogTitle>Title</DialogTitle>
454
+ <DialogDescription>Description</DialogDescription>
455
+ </DialogHeader>
456
+ {/* Content */}
457
+ <DialogFooter>{/* Actions */}</DialogFooter>
458
+ </DialogContent>
459
+ </Dialog>;
460
+ ```
461
+
462
+ ### Drawer
463
+
464
+ ```tsx
465
+ import {
466
+ Drawer,
467
+ DrawerTrigger,
468
+ DrawerContent,
469
+ DrawerHeader,
470
+ DrawerTitle,
471
+ } from "@algenium/blocks";
472
+
473
+ <Drawer>
474
+ <DrawerTrigger>Open</DrawerTrigger>
475
+ <DrawerContent>
476
+ <DrawerHeader>
477
+ <DrawerTitle>Title</DrawerTitle>
478
+ </DrawerHeader>
479
+ {/* Content */}
480
+ </DrawerContent>
481
+ </Drawer>;
482
+ ```
483
+
484
+ ### DropdownMenu
485
+
486
+ ```tsx
487
+ import {
488
+ DropdownMenu,
489
+ DropdownMenuTrigger,
490
+ DropdownMenuContent,
491
+ DropdownMenuItem,
492
+ } from "@algenium/blocks";
493
+
494
+ <DropdownMenu>
495
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
496
+ <DropdownMenuContent>
497
+ <DropdownMenuItem>Item 1</DropdownMenuItem>
498
+ <DropdownMenuItem>Item 2</DropdownMenuItem>
499
+ </DropdownMenuContent>
500
+ </DropdownMenu>;
501
+ ```
502
+
503
+ ### Popover
504
+
505
+ ```tsx
506
+ import { Popover, PopoverTrigger, PopoverContent } from "@algenium/blocks";
507
+
508
+ <Popover>
509
+ <PopoverTrigger>Open</PopoverTrigger>
510
+ <PopoverContent>Content here</PopoverContent>
511
+ </Popover>;
512
+ ```
513
+
514
+ ### ScrollArea
515
+
516
+ ```tsx
517
+ import { ScrollArea } from "@algenium/blocks";
518
+
519
+ <ScrollArea className="h-[200px]">{/* Scrollable content */}</ScrollArea>;
520
+ ```
521
+
522
+ ### Slider
523
+
524
+ ```tsx
525
+ import { Slider } from "@algenium/blocks";
526
+
527
+ <Slider
528
+ value={[50]}
529
+ onValueChange={(value) => setValue(value[0])}
530
+ max={100}
531
+ step={1}
532
+ />;
533
+ ```
534
+
535
+ ### Toggle
536
+
537
+ ```tsx
538
+ import { Toggle } from "@algenium/blocks";
539
+
540
+ <Toggle pressed={isPressed} onPressedChange={setIsPressed}>
541
+ Toggle me
542
+ </Toggle>;
543
+ ```
544
+
545
+ ### Tooltip
546
+
547
+ ```tsx
548
+ import {
549
+ Tooltip,
550
+ TooltipTrigger,
551
+ TooltipContent,
552
+ TooltipProvider,
553
+ } from "@algenium/blocks";
554
+
555
+ <TooltipProvider>
556
+ <Tooltip>
557
+ <TooltipTrigger>Hover me</TooltipTrigger>
558
+ <TooltipContent>Tooltip text</TooltipContent>
559
+ </Tooltip>
560
+ </TooltipProvider>;
561
+ ```
562
+
563
+ ---
564
+
565
+ ## Utilities
566
+
567
+ ### cn (Class Name utility)
568
+
569
+ A utility function for conditionally merging Tailwind CSS classes.
570
+
571
+ ```tsx
572
+ import { cn } from "@algenium/blocks";
573
+
574
+ const className = cn("base-class", condition && "conditional-class", {
575
+ "another-class": someCondition,
576
+ });
577
+ ```
578
+
579
+ ---
580
+
581
+ ## TypeScript Support
582
+
583
+ All components are fully typed with TypeScript. Import types as needed:
584
+
585
+ ```tsx
586
+ import type {
587
+ ThemeSwitcherProps,
588
+ LanguageSwitcherProps,
589
+ Language,
590
+ AvatarEditorProps,
591
+ AvatarEditorDialogProps,
592
+ NotificationsWidgetProps,
593
+ Notification,
594
+ NotificationType,
595
+ } from "@algenium/blocks";
596
+ ```
597
+
598
+ ---
599
+
600
+ ## Best Practices
601
+
602
+ ### Internationalization
603
+
604
+ All text-based components accept label props for i18n:
605
+
606
+ ```tsx
607
+ import { useTranslation } from "your-i18n-library";
608
+
609
+ const { t } = useTranslation();
610
+
611
+ <ThemeSwitcher
612
+ labels={{
613
+ theme: t("theme"),
614
+ light: t("light"),
615
+ dark: t("dark"),
616
+ system: t("system"),
617
+ }}
618
+ />;
619
+ ```
620
+
621
+ ### Theme Integration
622
+
623
+ Components use CSS variables for theming. Ensure your app provides these variables:
624
+
625
+ ```css
626
+ :root {
627
+ --background: 0 0% 100%;
628
+ --foreground: 222.2 84% 4.9%;
629
+ --primary: 222.2 47.4% 11.2%;
630
+ --muted: 210 40% 96.1%;
631
+ --border: 214.3 31.8% 91.4%;
632
+ /* ... other variables */
633
+ }
634
+ ```
635
+
636
+ ### Notification Management
637
+
638
+ For `NotificationsWidget`, implement a notification management system:
639
+
640
+ ```tsx
641
+ // Example notification manager
642
+ const [notifications, setNotifications] = useState<Notification[]>([]);
643
+
644
+ const markAsRead = (id: string) => {
645
+ setNotifications((prev) =>
646
+ prev.map((n) => (n.id === id ? { ...n, read: true } : n)),
647
+ );
648
+ };
649
+
650
+ const dismiss = (id: string) => {
651
+ setNotifications((prev) => prev.filter((n) => n.id !== id));
652
+ };
653
+
654
+ const addNotification = (notification: Omit<Notification, "id">) => {
655
+ const id = crypto.randomUUID();
656
+ setNotifications((prev) => [{ ...notification, id }, ...prev]);
657
+ };
658
+ ```
659
+
660
+ ---
661
+
662
+ ## Contributing
663
+
664
+ This package is maintained as part of the Algenium monorepo. See the main repository for contribution guidelines.
665
+
666
+ ## License
667
+
668
+ Proprietary - Algenium © 2026
669
+
670
+ ## Version
671
+
672
+ Current version: `1.0.0-rc.4`
673
+
674
+ ## Support
675
+
676
+ For issues or questions, contact the Algenium development team or open an issue in the repository.