@djangocfg/ui-core 2.1.90 → 2.1.92

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
@@ -1,6 +1,6 @@
1
1
  # @djangocfg/ui-core
2
2
 
3
- Pure React UI library with 60+ components built on Radix UI + Tailwind CSS v4.
3
+ Pure React UI library with 60 components built on Radix UI + Tailwind CSS v4.
4
4
 
5
5
  **No Next.js dependencies** — works with Electron, Vite, CRA, and any React environment.
6
6
 
@@ -21,7 +21,7 @@ pnpm add @djangocfg/ui-core
21
21
  | `@djangocfg/ui-core` | Electron, Vite, CRA, any React app |
22
22
  | `@djangocfg/ui-nextjs` | Next.js apps (extends ui-core) |
23
23
 
24
- ## Components (61)
24
+ ## Components (60)
25
25
 
26
26
  ### Forms (16)
27
27
  `Label` `Button` `ButtonLink` `Input` `Checkbox` `RadioGroup` `Select` `Textarea` `Switch` `Slider` `Combobox` `MultiSelect` `InputOTP` `PhoneInput` `Form` `Field`
@@ -32,8 +32,8 @@ pnpm add @djangocfg/ui-core
32
32
  ### Overlay (7)
33
33
  `Dialog` `AlertDialog` `Sheet` `Drawer` `Popover` `HoverCard` `Tooltip`
34
34
 
35
- ### Feedback (6)
36
- `Toast` `Toaster` `Alert` `Progress` `Badge` `Avatar`
35
+ ### Feedback (5)
36
+ `Toaster` `Alert` `Progress` `Badge` `Avatar`
37
37
 
38
38
  ### Data (8)
39
39
  `Table` `Tabs` `Accordion` `Collapsible` `Toggle` `ToggleGroup` `Calendar` `Carousel`
@@ -55,22 +55,21 @@ pnpm add @djangocfg/ui-core
55
55
  | `useDebounce` | Debounce values |
56
56
  | `useDebouncedCallback` | Debounced callbacks |
57
57
  | `useImageLoader` | Image loading state |
58
- | `useToast` | Toast notifications |
58
+ | `useToast` / `toast` | Toast notifications (Sonner) |
59
59
  | `useEventListener` | Event bus |
60
60
  | `useDebugTools` | Debug utilities |
61
61
 
62
62
  ## Usage
63
63
 
64
64
  ```tsx
65
- import { Button, Card, Input, useToast } from '@djangocfg/ui-core';
65
+ import { Button, Card, Input } from '@djangocfg/ui-core';
66
+ import { toast } from '@djangocfg/ui-core/hooks';
66
67
 
67
68
  function Example() {
68
- const { toast } = useToast();
69
-
70
69
  return (
71
70
  <Card>
72
71
  <Input placeholder="Email" />
73
- <Button onClick={() => toast({ title: 'Saved!' })}>
72
+ <Button onClick={() => toast.success('Saved!')}>
74
73
  Submit
75
74
  </Button>
76
75
  </Card>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-core",
3
- "version": "2.1.90",
3
+ "version": "2.1.92",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -53,7 +53,9 @@
53
53
  "react-dom": "^18.0.0 || ^19.0.0",
54
54
  "react-hook-form": "^7.0.0",
55
55
  "tailwindcss": "^4.0.0",
56
- "zod": "^4.0.0"
56
+ "zod": "^4.0.0",
57
+ "zustand": "^5.0.0",
58
+ "consola": "^3.4.2"
57
59
  },
58
60
  "dependencies": {
59
61
  "@hookform/resolvers": "^5.2.2",
@@ -102,7 +104,7 @@
102
104
  "vaul": "1.1.2"
103
105
  },
104
106
  "devDependencies": {
105
- "@djangocfg/typescript-config": "^2.1.90",
107
+ "@djangocfg/typescript-config": "^2.1.92",
106
108
  "@types/node": "^24.7.2",
107
109
  "@types/react": "^19.1.0",
108
110
  "@types/react-dom": "^19.1.0",
@@ -0,0 +1,276 @@
1
+ "use client"
2
+
3
+ import { AlertCircle, CheckCircle2, Download, Loader2 } from 'lucide-react';
4
+ import * as React from 'react';
5
+
6
+ import { Button, type ButtonProps } from './button';
7
+ import { cn } from '../lib';
8
+ import { useLocalStorage } from '../hooks/useLocalStorage';
9
+
10
+ // Token key used by the API client
11
+ const TOKEN_KEY = "auth_token"
12
+
13
+ export interface DownloadButtonProps extends Omit<ButtonProps, 'onClick'> {
14
+ /**
15
+ * URL to download from
16
+ */
17
+ url: string
18
+
19
+ /**
20
+ * Optional filename override. If not provided, will try to extract from Content-Disposition header
21
+ */
22
+ filename?: string
23
+
24
+ /**
25
+ * Optional callback when download starts
26
+ */
27
+ onDownloadStart?: () => void
28
+
29
+ /**
30
+ * Optional callback when download completes
31
+ */
32
+ onDownloadComplete?: (filename: string) => void
33
+
34
+ /**
35
+ * Optional callback when download fails
36
+ */
37
+ onDownloadError?: (error: Error) => void
38
+
39
+ /**
40
+ * Use fetch API for download (allows progress tracking and auth headers)
41
+ * Default: true
42
+ */
43
+ useFetch?: boolean
44
+
45
+ /**
46
+ * Show download status icons (loading, success, error)
47
+ * Default: true
48
+ */
49
+ showStatus?: boolean
50
+
51
+ /**
52
+ * Auto-reset status after success/error (in ms)
53
+ * Default: 2000
54
+ */
55
+ statusResetDelay?: number
56
+
57
+ /**
58
+ * Method for download (GET or POST)
59
+ * Default: GET
60
+ */
61
+ method?: 'GET' | 'POST'
62
+
63
+ /**
64
+ * Optional request body for POST requests
65
+ */
66
+ body?: any
67
+ }
68
+
69
+ type DownloadStatus = 'idle' | 'downloading' | 'success' | 'error'
70
+
71
+ const DownloadButton = React.forwardRef<HTMLButtonElement, DownloadButtonProps>(
72
+ (
73
+ {
74
+ url,
75
+ filename,
76
+ onDownloadStart,
77
+ onDownloadComplete,
78
+ onDownloadError,
79
+ useFetch = true,
80
+ showStatus = true,
81
+ statusResetDelay = 2000,
82
+ method = 'GET',
83
+ body,
84
+ children,
85
+ disabled,
86
+ className,
87
+ ...props
88
+ },
89
+ ref
90
+ ) => {
91
+ const [status, setStatus] = React.useState<DownloadStatus>('idle')
92
+ const resetTimeoutRef = React.useRef<NodeJS.Timeout | undefined>(undefined)
93
+
94
+ // Get auth token from localStorage
95
+ const [token] = useLocalStorage<string>(TOKEN_KEY, '')
96
+
97
+ // Clean up timeout on unmount
98
+ React.useEffect(() => {
99
+ return () => {
100
+ if (resetTimeoutRef.current) {
101
+ clearTimeout(resetTimeoutRef.current)
102
+ }
103
+ }
104
+ }, [])
105
+
106
+ /**
107
+ * Extract filename from Content-Disposition header
108
+ */
109
+ const extractFilename = (headers: Headers): string | null => {
110
+ const disposition = headers.get('Content-Disposition')
111
+ if (!disposition) return null
112
+
113
+ // Try to match: filename*=UTF-8''example.txt or filename="example.txt"
114
+ const filenameMatch = disposition.match(/filename\*?=(?:UTF-8'')?["']?([^"';]+)["']?/i)
115
+ if (filenameMatch && filenameMatch[1]) {
116
+ return decodeURIComponent(filenameMatch[1])
117
+ }
118
+
119
+ return null
120
+ }
121
+
122
+ /**
123
+ * Trigger browser download for blob
124
+ */
125
+ const triggerDownload = (blob: Blob, downloadFilename: string) => {
126
+ const url = window.URL.createObjectURL(blob)
127
+ const a = document.createElement('a')
128
+ a.href = url
129
+ a.download = downloadFilename
130
+ document.body.appendChild(a)
131
+ a.click()
132
+ document.body.removeChild(a)
133
+ window.URL.revokeObjectURL(url)
134
+ }
135
+
136
+ /**
137
+ * Reset status after delay
138
+ */
139
+ const scheduleStatusReset = () => {
140
+ if (resetTimeoutRef.current) {
141
+ clearTimeout(resetTimeoutRef.current)
142
+ }
143
+ resetTimeoutRef.current = setTimeout(() => {
144
+ setStatus('idle')
145
+ }, statusResetDelay)
146
+ }
147
+
148
+ /**
149
+ * Download using fetch API (supports auth, progress, error handling)
150
+ */
151
+ const downloadWithFetch = async () => {
152
+ try {
153
+ setStatus('downloading')
154
+ onDownloadStart?.()
155
+
156
+ const headers: HeadersInit = {}
157
+
158
+ // Add authorization header if token is available
159
+ if (token) {
160
+ headers['Authorization'] = `Bearer ${token}`
161
+ }
162
+
163
+ const options: RequestInit = {
164
+ method,
165
+ headers,
166
+ }
167
+
168
+ if (method === 'POST' && body) {
169
+ options.body = JSON.stringify(body)
170
+ options.headers = {
171
+ ...options.headers,
172
+ 'Content-Type': 'application/json',
173
+ }
174
+ }
175
+
176
+ const response = await fetch(url, options)
177
+
178
+ if (!response.ok) {
179
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`)
180
+ }
181
+
182
+ const blob = await response.blob()
183
+
184
+ // Determine filename
185
+ const downloadFilename =
186
+ filename ||
187
+ extractFilename(response.headers) ||
188
+ `download-${Date.now()}.bin`
189
+
190
+ // Trigger download
191
+ triggerDownload(blob, downloadFilename)
192
+
193
+ setStatus('success')
194
+ onDownloadComplete?.(downloadFilename)
195
+ scheduleStatusReset()
196
+ } catch (error) {
197
+ console.error('Download error:', error)
198
+ setStatus('error')
199
+ onDownloadError?.(error as Error)
200
+ scheduleStatusReset()
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Simple download using window.open (fallback)
206
+ */
207
+ const downloadWithWindowOpen = () => {
208
+ try {
209
+ setStatus('downloading')
210
+ onDownloadStart?.()
211
+ window.open(url, '_blank')
212
+
213
+ // We can't really track success/failure with window.open
214
+ // So just assume success after a short delay
215
+ setTimeout(() => {
216
+ setStatus('success')
217
+ onDownloadComplete?.(filename || 'file')
218
+ scheduleStatusReset()
219
+ }, 500)
220
+ } catch (error) {
221
+ console.error('Download error:', error)
222
+ setStatus('error')
223
+ onDownloadError?.(error as Error)
224
+ scheduleStatusReset()
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Handle download click
230
+ */
231
+ const handleDownload = () => {
232
+ if (status === 'downloading') return
233
+
234
+ if (useFetch) {
235
+ downloadWithFetch()
236
+ } else {
237
+ downloadWithWindowOpen()
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Render status icon
243
+ */
244
+ const renderStatusIcon = () => {
245
+ if (!showStatus) return null
246
+
247
+ switch (status) {
248
+ case 'downloading':
249
+ return <Loader2 className="animate-spin" />
250
+ case 'success':
251
+ return <CheckCircle2 className="text-green-600" />
252
+ case 'error':
253
+ return <AlertCircle className="text-red-600" />
254
+ default:
255
+ return <Download />
256
+ }
257
+ }
258
+
259
+ return (
260
+ <Button
261
+ ref={ref}
262
+ onClick={handleDownload}
263
+ disabled={disabled || status === 'downloading'}
264
+ className={cn(className)}
265
+ {...props}
266
+ >
267
+ {renderStatusIcon()}
268
+ {children}
269
+ </Button>
270
+ )
271
+ }
272
+ )
273
+
274
+ DownloadButton.displayName = "DownloadButton"
275
+
276
+ export { DownloadButton }
@@ -0,0 +1,219 @@
1
+ "use client"
2
+
3
+ import { Check, ChevronRight, Circle } from 'lucide-react';
4
+ import * as React from 'react';
5
+
6
+ import { cn } from '../lib';
7
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
8
+
9
+ const DropdownMenu = DropdownMenuPrimitive.Root
10
+
11
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
12
+
13
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group
14
+
15
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal
16
+
17
+ const DropdownMenuSub = DropdownMenuPrimitive.Sub
18
+
19
+ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
20
+
21
+ const DropdownMenuSubTrigger = React.forwardRef<
22
+ React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
23
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
24
+ inset?: boolean
25
+ }
26
+ >(({ className, inset, children, ...props }, ref) => (
27
+ <DropdownMenuPrimitive.SubTrigger
28
+ ref={ref}
29
+ className={cn(
30
+ "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
31
+ inset && "pl-8",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ {children}
37
+ <ChevronRight className="ml-auto" />
38
+ </DropdownMenuPrimitive.SubTrigger>
39
+ ))
40
+ DropdownMenuSubTrigger.displayName =
41
+ DropdownMenuPrimitive.SubTrigger.displayName
42
+
43
+ const DropdownMenuSubContent = React.forwardRef<
44
+ React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
45
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
46
+ >(({ className, ...props }, ref) => (
47
+ <DropdownMenuPrimitive.SubContent
48
+ ref={ref}
49
+ className={cn(
50
+ "z-600 min-w-32 overflow-hidden rounded-sm border bg-popover backdrop-blur-xl p-1 text-popover-foreground shadow-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
51
+ className
52
+ )}
53
+ {...props}
54
+ />
55
+ ))
56
+ DropdownMenuSubContent.displayName =
57
+ DropdownMenuPrimitive.SubContent.displayName
58
+
59
+ const DropdownMenuContent = React.forwardRef<
60
+ React.ElementRef<typeof DropdownMenuPrimitive.Content>,
61
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
62
+ >(({ className, sideOffset = 4, ...props }, ref) => (
63
+ <DropdownMenuPrimitive.Portal>
64
+ <DropdownMenuPrimitive.Content
65
+ ref={ref}
66
+ sideOffset={sideOffset}
67
+ className={cn(
68
+ "z-600 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-32 overflow-y-auto overflow-x-hidden rounded-sm border bg-popover backdrop-blur-xl p-1 text-popover-foreground shadow-sm",
69
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
70
+ className
71
+ )}
72
+ {...props}
73
+ />
74
+ </DropdownMenuPrimitive.Portal>
75
+ ))
76
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
77
+
78
+ const DropdownMenuItem = React.forwardRef<
79
+ React.ElementRef<typeof DropdownMenuPrimitive.Item>,
80
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
81
+ inset?: boolean
82
+ href?: string
83
+ key?: React.Key
84
+ }
85
+ >(({ className, inset, href, children, ...props }, ref) => {
86
+ const classes = cn(
87
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
88
+ inset && "pl-8",
89
+ className
90
+ )
91
+
92
+ if (href) {
93
+ return (
94
+ <DropdownMenuPrimitive.Item asChild ref={ref} {...props}>
95
+ <a href={href} className={classes}>
96
+ {children}
97
+ </a>
98
+ </DropdownMenuPrimitive.Item>
99
+ )
100
+ }
101
+
102
+ return (
103
+ <DropdownMenuPrimitive.Item
104
+ ref={ref}
105
+ className={classes}
106
+ {...props}
107
+ >
108
+ {children}
109
+ </DropdownMenuPrimitive.Item>
110
+ )
111
+ })
112
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
113
+
114
+ const DropdownMenuCheckboxItem = React.forwardRef<
115
+ React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
116
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> & { key?: React.Key }
117
+ >(({ className, children, checked, ...props }, ref) => (
118
+ <DropdownMenuPrimitive.CheckboxItem
119
+ ref={ref}
120
+ className={cn(
121
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
122
+ className
123
+ )}
124
+ checked={checked}
125
+ {...props}
126
+ >
127
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
128
+ <DropdownMenuPrimitive.ItemIndicator>
129
+ <Check className="h-4 w-4" />
130
+ </DropdownMenuPrimitive.ItemIndicator>
131
+ </span>
132
+ {children}
133
+ </DropdownMenuPrimitive.CheckboxItem>
134
+ ))
135
+ DropdownMenuCheckboxItem.displayName =
136
+ DropdownMenuPrimitive.CheckboxItem.displayName
137
+
138
+ const DropdownMenuRadioItem = React.forwardRef<
139
+ React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
140
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> & { key?: React.Key }
141
+ >(({ className, children, ...props }, ref) => (
142
+ <DropdownMenuPrimitive.RadioItem
143
+ ref={ref}
144
+ className={cn(
145
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
146
+ className
147
+ )}
148
+ {...props}
149
+ >
150
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
151
+ <DropdownMenuPrimitive.ItemIndicator>
152
+ <Circle className="h-2 w-2 fill-current" />
153
+ </DropdownMenuPrimitive.ItemIndicator>
154
+ </span>
155
+ {children}
156
+ </DropdownMenuPrimitive.RadioItem>
157
+ ))
158
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
159
+
160
+ const DropdownMenuLabel = React.forwardRef<
161
+ React.ElementRef<typeof DropdownMenuPrimitive.Label>,
162
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
163
+ inset?: boolean
164
+ }
165
+ >(({ className, inset, ...props }, ref) => (
166
+ <DropdownMenuPrimitive.Label
167
+ ref={ref}
168
+ className={cn(
169
+ "px-2 py-1.5 text-sm font-semibold",
170
+ inset && "pl-8",
171
+ className
172
+ )}
173
+ {...props}
174
+ />
175
+ ))
176
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
177
+
178
+ const DropdownMenuSeparator = React.forwardRef<
179
+ React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
180
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
181
+ >(({ className, ...props }, ref) => (
182
+ <DropdownMenuPrimitive.Separator
183
+ ref={ref}
184
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
185
+ {...props}
186
+ />
187
+ ))
188
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
189
+
190
+ const DropdownMenuShortcut = ({
191
+ className,
192
+ ...props
193
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
194
+ return (
195
+ <span
196
+ className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
197
+ {...props}
198
+ />
199
+ )
200
+ }
201
+ DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
202
+
203
+ export {
204
+ DropdownMenu,
205
+ DropdownMenuTrigger,
206
+ DropdownMenuContent,
207
+ DropdownMenuItem,
208
+ DropdownMenuCheckboxItem,
209
+ DropdownMenuRadioItem,
210
+ DropdownMenuLabel,
211
+ DropdownMenuSeparator,
212
+ DropdownMenuShortcut,
213
+ DropdownMenuGroup,
214
+ DropdownMenuPortal,
215
+ DropdownMenuSub,
216
+ DropdownMenuSubContent,
217
+ DropdownMenuSubTrigger,
218
+ DropdownMenuRadioGroup,
219
+ }
@@ -17,9 +17,15 @@ export { Combobox } from './combobox';
17
17
  export type { ComboboxOption, ComboboxProps } from './combobox';
18
18
  export { MultiSelect } from './multi-select';
19
19
  export type { MultiSelectOption, MultiSelectProps } from './multi-select';
20
+ export { MultiSelectPro } from './multi-select-pro';
21
+ export type { MultiSelectProOption, MultiSelectProGroup, MultiSelectProProps, MultiSelectProRef, AnimationConfig, ResponsiveConfig } from './multi-select-pro';
22
+ export { MultiSelectProAsync } from './multi-select-pro/async';
23
+ export type { MultiSelectProAsyncProps } from './multi-select-pro/async';
20
24
  export { Switch } from './switch';
21
25
  export { Slider } from './slider';
22
26
  export { InputOTP, InputOTPGroup, InputOTPSlot } from './input-otp';
27
+ export { OTPInput, InputOTPSeparator, useSmartOTP } from './otp';
28
+ export type { SmartOTPProps as OTPInputProps, OTPValidationMode, OTPPasteBehavior, OTPValidator } from './otp/types';
23
29
 
24
30
  // Layout Components
25
31
  export { Separator } from './separator';
@@ -64,11 +70,10 @@ export { Toggle } from './toggle';
64
70
  export { ToggleGroup, ToggleGroupItem } from './toggle-group';
65
71
  export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from './command';
66
72
  export { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from './context-menu';
73
+ export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuGroup, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuRadioGroup } from './dropdown-menu';
67
74
 
68
75
  // Feedback Components
69
76
  export { Alert, AlertDescription, AlertTitle } from './alert';
70
- export { Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from './toast';
71
- export { Toaster } from './toaster';
72
77
 
73
78
  // Form Components
74
79
  export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField } from './form';
@@ -85,5 +90,14 @@ export { Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldLegen
85
90
 
86
91
  export { CopyButton, CopyField } from './copy';
87
92
  export type { CopyButtonProps, CopyFieldProps } from './copy';
93
+ export { DownloadButton } from './button-download';
94
+ export type { DownloadButtonProps } from './button-download';
88
95
  export { OgImage } from './og-image';
89
96
  export type { OgImageProps } from './og-image';
97
+
98
+ // Additional Components
99
+ export { Toaster } from './sonner';
100
+ export { PhoneInput } from './phone-input';
101
+ export type { PhoneInputProps } from './phone-input';
102
+ export { navigationMenuTriggerStyle, NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuContent, NavigationMenuTrigger, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport } from './navigation-menu';
103
+ export { Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem, MenubarSeparator, MenubarLabel, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarPortal, MenubarSubContent, MenubarSubTrigger, MenubarGroup, MenubarSub, MenubarShortcut } from './menubar';