@janovix/blocks 1.0.0-rc.4 → 1.0.0-rc.6
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 +606 -28
- package/dist/index.cjs +46 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,632 @@ 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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
49
|
-
|
|
74
|
+
// Different sizes and shapes
|
|
75
|
+
<ThemeSwitcher size="sm" shape="pill" />;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Props:**
|
|
50
79
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
91
|
+
---
|
|
57
92
|
|
|
58
|
-
|
|
93
|
+
### LanguageSwitcher
|
|
59
94
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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 dialog wrapper for the AvatarEditor component with upload functionality.
|
|
236
|
+
|
|
237
|
+
**Features:**
|
|
238
|
+
|
|
239
|
+
- File upload with drag & drop
|
|
240
|
+
- Image preview
|
|
241
|
+
- Integrated AvatarEditor
|
|
242
|
+
- Cancel/Save actions
|
|
243
|
+
- File type validation (image/\*)
|
|
244
|
+
|
|
245
|
+
**Usage:**
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import { AvatarEditorDialog } from "@janovix/blocks";
|
|
249
|
+
|
|
250
|
+
<AvatarEditorDialog
|
|
251
|
+
isOpen={isOpen}
|
|
252
|
+
onOpenChange={setIsOpen}
|
|
253
|
+
onSave={(editedImage) => {
|
|
254
|
+
console.log("Saving image:", editedImage);
|
|
255
|
+
}}
|
|
256
|
+
title="Edit Avatar"
|
|
257
|
+
uploadLabel="Upload Image"
|
|
258
|
+
cancelLabel="Cancel"
|
|
259
|
+
saveLabel="Save"
|
|
260
|
+
/>;
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Props:**
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
interface AvatarEditorDialogProps {
|
|
267
|
+
isOpen: boolean;
|
|
268
|
+
onOpenChange: (open: boolean) => void;
|
|
269
|
+
onSave: (editedImage: string) => void;
|
|
270
|
+
title?: string;
|
|
271
|
+
uploadLabel?: string;
|
|
272
|
+
cancelLabel?: string;
|
|
273
|
+
saveLabel?: string;
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### NotificationsWidget
|
|
280
|
+
|
|
281
|
+
A compact notification bell widget with popover display, sound alerts, and animations.
|
|
282
|
+
|
|
283
|
+
**Features:**
|
|
284
|
+
|
|
285
|
+
- Bell icon with unread count badge
|
|
286
|
+
- Popover with scrollable notification list
|
|
287
|
+
- Multiple notification types (info, success, warning, error)
|
|
288
|
+
- Sound notifications (chime, bell, pop, ding)
|
|
289
|
+
- Pulse animations (ring, glow, bounce)
|
|
290
|
+
- Mark as read/dismiss actions
|
|
291
|
+
- Click-to-navigate support
|
|
292
|
+
- Customizable colors, sizes, and sounds
|
|
293
|
+
|
|
294
|
+
**Usage:**
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import { NotificationsWidget, Notification } from "@janovix/blocks";
|
|
298
|
+
|
|
299
|
+
const notifications: Notification[] = [
|
|
300
|
+
{
|
|
301
|
+
id: "1",
|
|
302
|
+
title: "New message",
|
|
303
|
+
message: "You have a new message from John",
|
|
304
|
+
type: "info",
|
|
305
|
+
timestamp: new Date(),
|
|
306
|
+
read: false,
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: "2",
|
|
310
|
+
title: "Task completed",
|
|
311
|
+
message: "Your export task finished successfully",
|
|
312
|
+
type: "success",
|
|
313
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 5),
|
|
314
|
+
read: false,
|
|
315
|
+
},
|
|
316
|
+
];
|
|
317
|
+
|
|
318
|
+
<NotificationsWidget
|
|
319
|
+
notifications={notifications}
|
|
320
|
+
onMarkAsRead={(id) => markAsRead(id)}
|
|
321
|
+
onMarkAllAsRead={() => markAllAsRead()}
|
|
322
|
+
onDismiss={(id) => dismiss(id)}
|
|
323
|
+
onClearAll={() => clearAll()}
|
|
324
|
+
onNotificationClick={(notification) => {
|
|
325
|
+
if (notification.href) {
|
|
326
|
+
router.push(notification.href);
|
|
327
|
+
}
|
|
328
|
+
}}
|
|
329
|
+
playSound
|
|
330
|
+
soundType="chime"
|
|
331
|
+
size="md"
|
|
332
|
+
dotColor="red"
|
|
333
|
+
pulseStyle="ring"
|
|
334
|
+
/>;
|
|
335
|
+
|
|
336
|
+
// Minimal usage
|
|
337
|
+
<NotificationsWidget
|
|
338
|
+
notifications={notifications}
|
|
339
|
+
onMarkAsRead={(id) => markAsRead(id)}
|
|
340
|
+
/>;
|
|
341
|
+
|
|
342
|
+
// Custom configuration
|
|
343
|
+
<NotificationsWidget
|
|
344
|
+
notifications={notifications}
|
|
345
|
+
onMarkAsRead={(id) => markAsRead(id)}
|
|
346
|
+
size="lg"
|
|
347
|
+
dotColor="primary"
|
|
348
|
+
soundType="bell"
|
|
349
|
+
pulseStyle="glow"
|
|
350
|
+
maxVisible={10}
|
|
351
|
+
title="Alerts"
|
|
352
|
+
emptyMessage="No alerts"
|
|
353
|
+
/>;
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Props:**
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
interface NotificationsWidgetProps {
|
|
360
|
+
notifications: Notification[];
|
|
361
|
+
onMarkAsRead?: (id: string) => void;
|
|
362
|
+
onMarkAllAsRead?: () => void;
|
|
363
|
+
onDismiss?: (id: string) => void;
|
|
364
|
+
onClearAll?: () => void;
|
|
365
|
+
onNotificationClick?: (notification: Notification) => void;
|
|
366
|
+
size?: "sm" | "md" | "lg";
|
|
367
|
+
maxVisible?: number; // Default: 5
|
|
368
|
+
playSound?: boolean; // Default: true
|
|
369
|
+
soundUrl?: string; // Custom sound file URL
|
|
370
|
+
soundType?: "chime" | "bell" | "pop" | "ding" | "none"; // Default: "chime"
|
|
371
|
+
soundCooldown?: number; // Default: 2000ms
|
|
372
|
+
className?: string;
|
|
373
|
+
emptyMessage?: string; // Default: "No notifications"
|
|
374
|
+
title?: string; // Default: "Notifications"
|
|
375
|
+
dotColor?: "red" | "blue" | "green" | "amber" | "purple" | "primary"; // Default: "red"
|
|
376
|
+
showPulse?: boolean; // Default: true
|
|
377
|
+
pulseStyle?: "ring" | "glow" | "bounce" | "none"; // Default: "ring"
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
interface Notification {
|
|
381
|
+
id: string;
|
|
382
|
+
title: string;
|
|
383
|
+
message?: string;
|
|
384
|
+
type?: "info" | "success" | "warning" | "error"; // Default: "info"
|
|
385
|
+
timestamp: Date;
|
|
386
|
+
read?: boolean;
|
|
387
|
+
href?: string; // Optional link for click navigation
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## UI Primitives
|
|
394
|
+
|
|
395
|
+
The package also exports underlying UI primitives that can be used independently:
|
|
396
|
+
|
|
397
|
+
### Button
|
|
398
|
+
|
|
399
|
+
```tsx
|
|
400
|
+
import { Button } from "@janovix/blocks";
|
|
401
|
+
|
|
402
|
+
<Button variant="default">Click me</Button>;
|
|
403
|
+
<Button variant="outline" size="sm">
|
|
404
|
+
Small
|
|
405
|
+
</Button>;
|
|
406
|
+
<Button variant="ghost" size="icon">
|
|
407
|
+
<Icon />
|
|
408
|
+
</Button>;
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Dialog
|
|
412
|
+
|
|
413
|
+
```tsx
|
|
414
|
+
import {
|
|
415
|
+
Dialog,
|
|
416
|
+
DialogTrigger,
|
|
417
|
+
DialogContent,
|
|
418
|
+
DialogHeader,
|
|
419
|
+
DialogTitle,
|
|
420
|
+
DialogDescription,
|
|
421
|
+
DialogFooter,
|
|
422
|
+
} from "@janovix/blocks";
|
|
423
|
+
|
|
424
|
+
<Dialog>
|
|
425
|
+
<DialogTrigger>Open</DialogTrigger>
|
|
426
|
+
<DialogContent>
|
|
427
|
+
<DialogHeader>
|
|
428
|
+
<DialogTitle>Title</DialogTitle>
|
|
429
|
+
<DialogDescription>Description</DialogDescription>
|
|
430
|
+
</DialogHeader>
|
|
431
|
+
{/* Content */}
|
|
432
|
+
<DialogFooter>{/* Actions */}</DialogFooter>
|
|
433
|
+
</DialogContent>
|
|
434
|
+
</Dialog>;
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Drawer
|
|
438
|
+
|
|
439
|
+
```tsx
|
|
440
|
+
import {
|
|
441
|
+
Drawer,
|
|
442
|
+
DrawerTrigger,
|
|
443
|
+
DrawerContent,
|
|
444
|
+
DrawerHeader,
|
|
445
|
+
DrawerTitle,
|
|
446
|
+
} from "@janovix/blocks";
|
|
447
|
+
|
|
448
|
+
<Drawer>
|
|
449
|
+
<DrawerTrigger>Open</DrawerTrigger>
|
|
450
|
+
<DrawerContent>
|
|
451
|
+
<DrawerHeader>
|
|
452
|
+
<DrawerTitle>Title</DrawerTitle>
|
|
453
|
+
</DrawerHeader>
|
|
454
|
+
{/* Content */}
|
|
455
|
+
</DrawerContent>
|
|
456
|
+
</Drawer>;
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### DropdownMenu
|
|
460
|
+
|
|
461
|
+
```tsx
|
|
462
|
+
import {
|
|
463
|
+
DropdownMenu,
|
|
464
|
+
DropdownMenuTrigger,
|
|
465
|
+
DropdownMenuContent,
|
|
466
|
+
DropdownMenuItem,
|
|
467
|
+
} from "@janovix/blocks";
|
|
468
|
+
|
|
469
|
+
<DropdownMenu>
|
|
470
|
+
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
|
|
471
|
+
<DropdownMenuContent>
|
|
472
|
+
<DropdownMenuItem>Item 1</DropdownMenuItem>
|
|
473
|
+
<DropdownMenuItem>Item 2</DropdownMenuItem>
|
|
474
|
+
</DropdownMenuContent>
|
|
475
|
+
</DropdownMenu>;
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Popover
|
|
479
|
+
|
|
480
|
+
```tsx
|
|
481
|
+
import { Popover, PopoverTrigger, PopoverContent } from "@janovix/blocks";
|
|
482
|
+
|
|
483
|
+
<Popover>
|
|
484
|
+
<PopoverTrigger>Open</PopoverTrigger>
|
|
485
|
+
<PopoverContent>Content here</PopoverContent>
|
|
486
|
+
</Popover>;
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### ScrollArea
|
|
490
|
+
|
|
491
|
+
```tsx
|
|
492
|
+
import { ScrollArea } from "@janovix/blocks";
|
|
493
|
+
|
|
494
|
+
<ScrollArea className="h-[200px]">{/* Scrollable content */}</ScrollArea>;
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Slider
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
import { Slider } from "@janovix/blocks";
|
|
501
|
+
|
|
502
|
+
<Slider
|
|
503
|
+
value={[50]}
|
|
504
|
+
onValueChange={(value) => setValue(value[0])}
|
|
505
|
+
max={100}
|
|
506
|
+
step={1}
|
|
507
|
+
/>;
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Toggle
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
import { Toggle } from "@janovix/blocks";
|
|
514
|
+
|
|
515
|
+
<Toggle pressed={isPressed} onPressedChange={setIsPressed}>
|
|
516
|
+
Toggle me
|
|
517
|
+
</Toggle>;
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Tooltip
|
|
521
|
+
|
|
522
|
+
```tsx
|
|
523
|
+
import {
|
|
524
|
+
Tooltip,
|
|
525
|
+
TooltipTrigger,
|
|
526
|
+
TooltipContent,
|
|
527
|
+
TooltipProvider,
|
|
528
|
+
} from "@janovix/blocks";
|
|
529
|
+
|
|
530
|
+
<TooltipProvider>
|
|
531
|
+
<Tooltip>
|
|
532
|
+
<TooltipTrigger>Hover me</TooltipTrigger>
|
|
533
|
+
<TooltipContent>Tooltip text</TooltipContent>
|
|
534
|
+
</Tooltip>
|
|
535
|
+
</TooltipProvider>;
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## Utilities
|
|
541
|
+
|
|
542
|
+
### cn (Class Name utility)
|
|
543
|
+
|
|
544
|
+
A utility function for conditionally merging Tailwind CSS classes.
|
|
545
|
+
|
|
546
|
+
```tsx
|
|
547
|
+
import { cn } from "@janovix/blocks";
|
|
548
|
+
|
|
549
|
+
const className = cn("base-class", condition && "conditional-class", {
|
|
550
|
+
"another-class": someCondition,
|
|
551
|
+
});
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## TypeScript Support
|
|
557
|
+
|
|
558
|
+
All components are fully typed with TypeScript. Import types as needed:
|
|
559
|
+
|
|
560
|
+
```tsx
|
|
561
|
+
import type {
|
|
562
|
+
ThemeSwitcherProps,
|
|
563
|
+
LanguageSwitcherProps,
|
|
564
|
+
Language,
|
|
565
|
+
AvatarEditorProps,
|
|
566
|
+
AvatarEditorDialogProps,
|
|
567
|
+
NotificationsWidgetProps,
|
|
568
|
+
Notification,
|
|
569
|
+
NotificationType,
|
|
570
|
+
} from "@janovix/blocks";
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## Best Practices
|
|
576
|
+
|
|
577
|
+
### Internationalization
|
|
578
|
+
|
|
579
|
+
All text-based components accept label props for i18n:
|
|
580
|
+
|
|
581
|
+
```tsx
|
|
582
|
+
import { useTranslation } from "your-i18n-library";
|
|
583
|
+
|
|
584
|
+
const { t } = useTranslation();
|
|
585
|
+
|
|
586
|
+
<ThemeSwitcher
|
|
587
|
+
labels={{
|
|
588
|
+
theme: t("theme"),
|
|
589
|
+
light: t("light"),
|
|
590
|
+
dark: t("dark"),
|
|
591
|
+
system: t("system"),
|
|
592
|
+
}}
|
|
593
|
+
/>;
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Theme Integration
|
|
597
|
+
|
|
598
|
+
Components use CSS variables for theming. Ensure your app provides these variables:
|
|
599
|
+
|
|
600
|
+
```css
|
|
601
|
+
:root {
|
|
602
|
+
--background: 0 0% 100%;
|
|
603
|
+
--foreground: 222.2 84% 4.9%;
|
|
604
|
+
--primary: 222.2 47.4% 11.2%;
|
|
605
|
+
--muted: 210 40% 96.1%;
|
|
606
|
+
--border: 214.3 31.8% 91.4%;
|
|
607
|
+
/* ... other variables */
|
|
608
|
+
}
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### Notification Management
|
|
612
|
+
|
|
613
|
+
For `NotificationsWidget`, implement a notification management system:
|
|
614
|
+
|
|
615
|
+
```tsx
|
|
616
|
+
// Example notification manager
|
|
617
|
+
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
618
|
+
|
|
619
|
+
const markAsRead = (id: string) => {
|
|
620
|
+
setNotifications((prev) =>
|
|
621
|
+
prev.map((n) => (n.id === id ? { ...n, read: true } : n)),
|
|
622
|
+
);
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const dismiss = (id: string) => {
|
|
626
|
+
setNotifications((prev) => prev.filter((n) => n.id !== id));
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
const addNotification = (notification: Omit<Notification, "id">) => {
|
|
630
|
+
const id = crypto.randomUUID();
|
|
631
|
+
setNotifications((prev) => [{ ...notification, id }, ...prev]);
|
|
68
632
|
};
|
|
69
633
|
```
|
|
70
634
|
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## Contributing
|
|
638
|
+
|
|
639
|
+
This package is maintained as part of the Janovix monorepo. See the main repository for contribution guidelines.
|
|
640
|
+
|
|
71
641
|
## License
|
|
72
642
|
|
|
73
|
-
|
|
643
|
+
Proprietary - Janovix © 2026
|
|
644
|
+
|
|
645
|
+
## Version
|
|
646
|
+
|
|
647
|
+
Current version: `1.0.0-rc.4`
|
|
648
|
+
|
|
649
|
+
## Support
|
|
650
|
+
|
|
651
|
+
For issues or questions, contact the Janovix development team or open an issue in the repository.
|