@janovix/blocks 1.0.0 → 1.1.0

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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  > Shared UI components for Janovix applications
4
4
 
5
+ A collection of reusable, accessible UI components built with React, Radix UI, and Tailwind CSS. Designed for use across all Janovix applications.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
@@ -18,56 +20,657 @@ This package requires the following peer dependencies:
18
20
  pnpm add react react-dom next-themes lucide-react motion
19
21
  ```
20
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/@janovix/blocks/dist/**/*.js",
35
+ ],
36
+ // ... rest of your config
37
+ };
38
+ ```
39
+
21
40
  ## Components
22
41
 
23
42
  ### ThemeSwitcher
24
43
 
25
- A theme switcher component that integrates with `next-themes`.
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:**
26
54
 
27
55
  ```tsx
28
56
  import { ThemeSwitcher } from "@janovix/blocks";
29
57
 
30
58
  // Default usage
31
- <ThemeSwitcher />
59
+ <ThemeSwitcher />;
32
60
 
33
61
  // With custom labels (for i18n)
34
62
  <ThemeSwitcher
35
- labels={{
36
- theme: "Theme",
37
- system: "System",
38
- light: "Light",
39
- dark: "Dark",
40
- }}
41
- />
63
+ labels={{
64
+ theme: "Theme",
65
+ system: "System",
66
+ light: "Light",
67
+ dark: "Dark",
68
+ }}
69
+ />;
42
70
 
43
71
  // Mini variant (icon only with dropdown)
44
- <ThemeSwitcher variant="mini" />
72
+ <ThemeSwitcher variant="mini" />;
45
73
 
46
- // Different sizes
47
- <ThemeSwitcher size="sm" />
48
- <ThemeSwitcher size="md" />
49
- <ThemeSwitcher size="lg" />
74
+ // Different sizes and shapes
75
+ <ThemeSwitcher size="sm" shape="pill" />;
76
+ ```
77
+
78
+ **Props:**
50
79
 
51
- // Different shapes
52
- <ThemeSwitcher shape="rounded" />
53
- <ThemeSwitcher shape="pill" />
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
+ }
54
89
  ```
55
90
 
56
- ## Styling
91
+ ---
57
92
 
58
- This package uses Tailwind CSS classes. You need to configure your Tailwind CSS to include this package:
93
+ ### LanguageSwitcher
59
94
 
60
- ```js
61
- // tailwind.config.js
62
- module.exports = {
63
- content: [
64
- // ... your other content paths
65
- "./node_modules/@janovix/blocks/dist/**/*.js",
66
- ],
67
- // ... rest of your config
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 "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/blocks";
518
+
519
+ <ScrollArea className="h-[200px]">{/* Scrollable content */}</ScrollArea>;
520
+ ```
521
+
522
+ ### Slider
523
+
524
+ ```tsx
525
+ import { Slider } from "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/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 "@janovix/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]);
68
657
  };
69
658
  ```
70
659
 
660
+ ---
661
+
662
+ ## Contributing
663
+
664
+ This package is maintained as part of the Janovix monorepo. See the main repository for contribution guidelines.
665
+
71
666
  ## License
72
667
 
73
- MIT
668
+ Proprietary - Janovix © 2026
669
+
670
+ ## Version
671
+
672
+ Current version: `1.0.0-rc.4`
673
+
674
+ ## Support
675
+
676
+ For issues or questions, contact the Janovix development team or open an issue in the repository.