@olwiba/ui 0.1.13 → 0.1.15

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.
@@ -0,0 +1,94 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { toast } from 'sonner';
5
+ import { CheckCircle2, Inbox, Info, X } from 'lucide-react';
6
+ import { cn } from '@olwiba/cn';
7
+ import { Button } from '../primitives/Button';
8
+
9
+ export interface NotifyAction {
10
+ label: string;
11
+ onClick: () => void;
12
+ }
13
+
14
+ export interface NotificationToastProps {
15
+ variant?: 'success' | 'info' | 'message';
16
+ title: string;
17
+ description?: string;
18
+ /** Avatar image — overrides the variant icon (e.g. for a message-from-a-person toast). */
19
+ avatar?: string;
20
+ /** Primary action, right-aligned next to the description (e.g. "Undo"). */
21
+ action?: NotifyAction;
22
+ /** Secondary action, rendered after the primary one (e.g. "Decline"). */
23
+ secondaryAction?: NotifyAction;
24
+ onDismiss?: () => void;
25
+ }
26
+
27
+ const variantIcon = {
28
+ success: <CheckCircle2 className="size-5 text-primary" />,
29
+ info: <Info className="size-5 text-muted-foreground" />,
30
+ message: <Inbox className="size-5 text-muted-foreground" />,
31
+ } as const;
32
+
33
+ /**
34
+ * Rich toast content — rendered via `notify()` inside sonner's `toast.custom`.
35
+ * One component: `variant` swaps the default icon, `avatar`/`action`/
36
+ * `secondaryAction` add the pieces a given toast needs, rather than a
37
+ * separate toast component per shape.
38
+ */
39
+ export function NotificationToast({
40
+ variant = 'info',
41
+ title,
42
+ description,
43
+ avatar,
44
+ action,
45
+ secondaryAction,
46
+ onDismiss,
47
+ }: NotificationToastProps) {
48
+ return (
49
+ <div className="w-full max-w-sm rounded-lg border bg-card p-4 text-card-foreground shadow-lg">
50
+ <div className="flex items-start gap-3">
51
+ <div className="shrink-0 pt-0.5">
52
+ {avatar ? (
53
+ // eslint-disable-next-line @next/next/no-img-element
54
+ <img src={avatar} alt="" className="size-9 rounded-full border object-cover" />
55
+ ) : (
56
+ variantIcon[variant]
57
+ )}
58
+ </div>
59
+ <div className="min-w-0 flex-1 pt-0.5">
60
+ <p className="text-sm font-medium">{title}</p>
61
+ {description && <p className="mt-1 text-sm text-muted-foreground">{description}</p>}
62
+ {(action || secondaryAction) && (
63
+ <div className={cn('mt-3 flex gap-4', avatar && 'gap-3')}>
64
+ {action && (
65
+ <button type="button" onClick={action.onClick} className="text-sm font-medium text-primary hover:underline">
66
+ {action.label}
67
+ </button>
68
+ )}
69
+ {secondaryAction && (
70
+ <button type="button" onClick={secondaryAction.onClick} className="text-sm font-medium text-muted-foreground hover:text-foreground">
71
+ {secondaryAction.label}
72
+ </button>
73
+ )}
74
+ </div>
75
+ )}
76
+ </div>
77
+ <Button variant="ghost" size="icon" className="size-6 shrink-0 -mt-1 -mr-1" onClick={onDismiss}>
78
+ <X className="size-4" />
79
+ <span className="sr-only">Dismiss</span>
80
+ </Button>
81
+ </div>
82
+ </div>
83
+ );
84
+ }
85
+
86
+ export interface NotifyOptions extends Omit<NotificationToastProps, 'onDismiss'> {
87
+ duration?: number;
88
+ }
89
+
90
+ /** Fires a `NotificationToast` through sonner. Requires `<Toaster />` from `@olwiba/cn` mounted once in your app. */
91
+ export function notify(options: NotifyOptions) {
92
+ const { duration, ...toastProps } = options;
93
+ return toast.custom((id) => <NotificationToast {...toastProps} onDismiss={() => toast.dismiss(id)} />, { duration });
94
+ }
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export {
13
13
 
14
14
  // ─── Primitives — mode-aware re-exports of @olwiba/cn components ─────────────
15
15
  export * from './primitives';
16
+ export { Enchanted, type EnchantedProps } from '@olwiba/cn';
16
17
 
17
18
  // ─── Layout — grid, stack, section primitives ────────────────────────────────
18
19
  export * from './layout';
@@ -34,6 +35,31 @@ export {
34
35
  type AuthFormProps,
35
36
  } from './app/AuthSection';
36
37
 
38
+ export {
39
+ SettingsSection,
40
+ type SettingsSectionProps,
41
+ } from './app/SettingsSection';
42
+
43
+ export {
44
+ TeamMembersPanel,
45
+ type TeamMembersPanelProps,
46
+ type TeamMemberRecord,
47
+ } from './app/TeamMembersPanel';
48
+
49
+ export {
50
+ BillingPanel,
51
+ type BillingPanelProps,
52
+ type BillingUsageMetric,
53
+ type BillingInvoice,
54
+ type BillingPaymentMethod,
55
+ } from './app/BillingPanel';
56
+
57
+ export {
58
+ OnboardingWizard,
59
+ type OnboardingWizardProps,
60
+ type OnboardingStep,
61
+ } from './app/OnboardingWizard';
62
+
37
63
  export {
38
64
  EmptyState,
39
65
  type EmptyStateProps,
@@ -94,6 +120,14 @@ export { Spotlight, type SpotlightProps, type SpotlightGroup, type SpotlightItem
94
120
  export { Dock, type DockProps, type DockItem } from './components/Dock';
95
121
  export { ContextMenu, type ContextMenuProps, type ContextMenuDef } from './components/ContextMenu';
96
122
  export { ConfirmDialog, type ConfirmDialogProps } from './components/ConfirmDialog';
123
+ export { CommandMenu, type CommandMenuProps, type CommandMenuGroup, type CommandMenuItem } from './components/CommandMenu';
124
+
125
+ // ─── Components — data ───────────────────────────────────────────────────────
126
+ export { DataTable, type DataTableProps, type DataTableColumn } from './components/DataTable';
127
+ export { FileUpload, type FileUploadProps, type FileUploadEntry } from './components/FileUpload';
128
+
129
+ // ─── Components — notifications ──────────────────────────────────────────────
130
+ export { NotificationToast, type NotificationToastProps, notify, type NotifyOptions, type NotifyAction } from './components/Notify';
97
131
 
98
132
  // ─── Components — device mockups ────────────────────────────────────────────
99
133
  export { PhoneFrame, type PhoneFrameProps } from './components/PhoneFrame';