@moontra/moonui-pro 2.20.2 → 2.20.4

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.
Files changed (153) hide show
  1. package/package.json +8 -3
  2. package/plugin/index.d.ts +86 -0
  3. package/plugin/index.js +308 -0
  4. package/scripts/postinstall.js +191 -23
  5. package/src/components/advanced-chart/index.tsx +0 -1246
  6. package/src/components/advanced-forms/index.tsx +0 -585
  7. package/src/components/animated-button/index.tsx +0 -385
  8. package/src/components/calendar/event-dialog.tsx +0 -377
  9. package/src/components/calendar/index.tsx +0 -1220
  10. package/src/components/calendar-pro/index.tsx +0 -1697
  11. package/src/components/color-picker/index.tsx +0 -432
  12. package/src/components/credit-card-input/index.tsx +0 -406
  13. package/src/components/dashboard/dashboard-grid.tsx +0 -480
  14. package/src/components/dashboard/demo.tsx +0 -425
  15. package/src/components/dashboard/index.tsx +0 -1046
  16. package/src/components/dashboard/time-range-picker.tsx +0 -336
  17. package/src/components/dashboard/types.ts +0 -225
  18. package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
  19. package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
  20. package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
  21. package/src/components/dashboard/widgets/index.ts +0 -5
  22. package/src/components/dashboard/widgets/metric-card.tsx +0 -363
  23. package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
  24. package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
  25. package/src/components/data-table/data-table-column-toggle.tsx +0 -169
  26. package/src/components/data-table/data-table-export.ts +0 -156
  27. package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
  28. package/src/components/data-table/index.tsx +0 -845
  29. package/src/components/draggable-list/index.tsx +0 -100
  30. package/src/components/error-boundary/index.tsx +0 -232
  31. package/src/components/file-upload/index.tsx +0 -1660
  32. package/src/components/floating-action-button/index.tsx +0 -206
  33. package/src/components/form-wizard/form-wizard-context.tsx +0 -335
  34. package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
  35. package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
  36. package/src/components/form-wizard/form-wizard-step.tsx +0 -111
  37. package/src/components/form-wizard/index.tsx +0 -102
  38. package/src/components/form-wizard/types.ts +0 -77
  39. package/src/components/gesture-drawer/index.tsx +0 -551
  40. package/src/components/github-stars/github-api.ts +0 -426
  41. package/src/components/github-stars/hooks.ts +0 -517
  42. package/src/components/github-stars/index.tsx +0 -375
  43. package/src/components/github-stars/types.ts +0 -148
  44. package/src/components/github-stars/variants.tsx +0 -515
  45. package/src/components/health-check/index.tsx +0 -439
  46. package/src/components/hover-card-3d/index.tsx +0 -529
  47. package/src/components/index.ts +0 -130
  48. package/src/components/internal/index.ts +0 -78
  49. package/src/components/kanban/add-card-modal.tsx +0 -502
  50. package/src/components/kanban/card-detail-modal.tsx +0 -761
  51. package/src/components/kanban/index.ts +0 -13
  52. package/src/components/kanban/kanban.tsx +0 -1689
  53. package/src/components/kanban/types.ts +0 -168
  54. package/src/components/lazy-component/index.tsx +0 -823
  55. package/src/components/license-error/index.tsx +0 -31
  56. package/src/components/magnetic-button/index.tsx +0 -216
  57. package/src/components/memory-efficient-data/index.tsx +0 -1018
  58. package/src/components/moonui-quiz-form/index.tsx +0 -817
  59. package/src/components/navbar/index.tsx +0 -781
  60. package/src/components/optimized-image/index.tsx +0 -425
  61. package/src/components/performance-debugger/index.tsx +0 -613
  62. package/src/components/performance-monitor/index.tsx +0 -808
  63. package/src/components/phone-number-input/index.tsx +0 -343
  64. package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
  65. package/src/components/pinch-zoom/index.tsx +0 -566
  66. package/src/components/quiz-form/index.tsx +0 -479
  67. package/src/components/rich-text-editor/index.tsx +0 -2322
  68. package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
  69. package/src/components/rich-text-editor/slash-commands.css +0 -35
  70. package/src/components/rich-text-editor/table-styles.css +0 -65
  71. package/src/components/sidebar/index.tsx +0 -884
  72. package/src/components/spotlight-card/index.tsx +0 -191
  73. package/src/components/swipeable-card/index.tsx +0 -100
  74. package/src/components/timeline/index.tsx +0 -1183
  75. package/src/components/ui/accordion.tsx +0 -581
  76. package/src/components/ui/alert-dialog.tsx +0 -141
  77. package/src/components/ui/alert.tsx +0 -141
  78. package/src/components/ui/aspect-ratio.tsx +0 -245
  79. package/src/components/ui/avatar.tsx +0 -155
  80. package/src/components/ui/badge.tsx +0 -230
  81. package/src/components/ui/breadcrumb.tsx +0 -216
  82. package/src/components/ui/button.tsx +0 -228
  83. package/src/components/ui/calendar.tsx +0 -387
  84. package/src/components/ui/card.tsx +0 -216
  85. package/src/components/ui/checkbox.tsx +0 -259
  86. package/src/components/ui/collapsible.tsx +0 -631
  87. package/src/components/ui/color-picker.tsx +0 -97
  88. package/src/components/ui/command.tsx +0 -948
  89. package/src/components/ui/dialog.tsx +0 -752
  90. package/src/components/ui/dropdown-menu.tsx +0 -706
  91. package/src/components/ui/gesture-drawer.tsx +0 -11
  92. package/src/components/ui/hover-card.tsx +0 -29
  93. package/src/components/ui/index.ts +0 -222
  94. package/src/components/ui/input.tsx +0 -224
  95. package/src/components/ui/label.tsx +0 -29
  96. package/src/components/ui/lightbox.tsx +0 -606
  97. package/src/components/ui/magnetic-button.tsx +0 -129
  98. package/src/components/ui/media-gallery.tsx +0 -611
  99. package/src/components/ui/navigation-menu.tsx +0 -130
  100. package/src/components/ui/pagination.tsx +0 -125
  101. package/src/components/ui/popover.tsx +0 -185
  102. package/src/components/ui/progress.tsx +0 -30
  103. package/src/components/ui/radio-group.tsx +0 -257
  104. package/src/components/ui/scroll-area.tsx +0 -47
  105. package/src/components/ui/select.tsx +0 -378
  106. package/src/components/ui/separator.tsx +0 -145
  107. package/src/components/ui/sheet.tsx +0 -139
  108. package/src/components/ui/skeleton.tsx +0 -20
  109. package/src/components/ui/slider.tsx +0 -354
  110. package/src/components/ui/spotlight-card.tsx +0 -119
  111. package/src/components/ui/switch.tsx +0 -86
  112. package/src/components/ui/table.tsx +0 -331
  113. package/src/components/ui/tabs-pro.tsx +0 -542
  114. package/src/components/ui/tabs.tsx +0 -54
  115. package/src/components/ui/textarea.tsx +0 -28
  116. package/src/components/ui/toast.tsx +0 -317
  117. package/src/components/ui/toggle.tsx +0 -119
  118. package/src/components/ui/tooltip.tsx +0 -151
  119. package/src/components/virtual-list/index.tsx +0 -668
  120. package/src/hooks/use-chart.ts +0 -205
  121. package/src/hooks/use-data-table.ts +0 -182
  122. package/src/hooks/use-docs-pro-access.ts +0 -13
  123. package/src/hooks/use-license-check.ts +0 -65
  124. package/src/hooks/use-subscription.ts +0 -19
  125. package/src/hooks/use-toast.ts +0 -15
  126. package/src/index.ts +0 -22
  127. package/src/lib/ai-providers.ts +0 -377
  128. package/src/lib/component-metadata.ts +0 -18
  129. package/src/lib/micro-interactions.ts +0 -255
  130. package/src/lib/paddle.ts +0 -17
  131. package/src/lib/utils.ts +0 -6
  132. package/src/patterns/login-form/index.tsx +0 -276
  133. package/src/patterns/login-form/types.ts +0 -67
  134. package/src/setupTests.ts +0 -41
  135. package/src/styles/advanced-chart.css +0 -239
  136. package/src/styles/calendar.css +0 -35
  137. package/src/styles/design-system.css +0 -363
  138. package/src/styles/index.css +0 -681
  139. package/src/styles/tailwind.css +0 -7
  140. package/src/styles/tokens.css +0 -455
  141. package/src/types/next-auth.d.ts +0 -21
  142. package/src/use-intersection-observer.tsx +0 -154
  143. package/src/use-local-storage.tsx +0 -71
  144. package/src/use-paddle.ts +0 -138
  145. package/src/use-performance-optimizer.ts +0 -389
  146. package/src/use-pro-access.ts +0 -141
  147. package/src/use-scroll-animation.ts +0 -219
  148. package/src/use-subscription.ts +0 -37
  149. package/src/use-toast.ts +0 -32
  150. package/src/utils/chart-helpers.ts +0 -357
  151. package/src/utils/cn.ts +0 -6
  152. package/src/utils/data-processing.ts +0 -151
  153. package/src/utils/license-validator.tsx +0 -183
@@ -1,781 +0,0 @@
1
- "use client"
2
-
3
- import React, { useState, useEffect, useCallback, useRef } from 'react'
4
- import { motion, AnimatePresence } from 'framer-motion'
5
- import { cn } from '../../lib/utils'
6
- import { Button } from '../ui/button'
7
- import { Badge } from '../ui/badge'
8
- import { Separator } from '../ui/separator'
9
- import { Sheet, SheetContent, SheetTrigger } from '../ui/sheet'
10
- import {
11
- DropdownMenu,
12
- DropdownMenuContent,
13
- DropdownMenuItem,
14
- DropdownMenuSeparator,
15
- DropdownMenuTrigger,
16
- DropdownMenuSub,
17
- DropdownMenuSubContent,
18
- DropdownMenuSubTrigger,
19
- } from '../ui/dropdown-menu'
20
- import {
21
- NavigationMenu,
22
- NavigationMenuContent,
23
- NavigationMenuItem,
24
- NavigationMenuLink,
25
- NavigationMenuList,
26
- NavigationMenuTrigger,
27
- navigationMenuTriggerStyle,
28
- } from '../ui/navigation-menu'
29
- import {
30
- Menu,
31
- X,
32
- ChevronDown,
33
- Search,
34
- Bell,
35
- User,
36
- Settings,
37
- LogOut,
38
- Moon,
39
- Sun,
40
- Monitor,
41
- Command,
42
- Sparkles
43
- } from 'lucide-react'
44
- import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'
45
- import { Input } from '../ui/input'
46
- import {
47
- CommandDialog,
48
- CommandEmpty,
49
- CommandGroup,
50
- CommandInput,
51
- CommandItem,
52
- CommandList,
53
- } from '../ui/command'
54
-
55
- export interface NavbarItem {
56
- id: string
57
- title: string
58
- href?: string
59
- icon?: React.ReactNode
60
- badge?: string | number
61
- badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline'
62
- disabled?: boolean
63
- items?: NavbarItem[]
64
- action?: () => void
65
- description?: string
66
- thumbnail?: string
67
- }
68
-
69
- export interface NavbarSection {
70
- id: string
71
- items: NavbarItem[]
72
- showInMobile?: boolean
73
- }
74
-
75
- export interface NavbarConfig {
76
- sections: NavbarSection[]
77
- branding?: {
78
- logo?: React.ReactNode
79
- title?: string
80
- href?: string
81
- }
82
- showSearch?: boolean
83
- searchPlaceholder?: string
84
- onSearch?: (value: string) => void
85
- showThemeToggle?: boolean
86
- theme?: 'light' | 'dark' | 'system'
87
- onThemeChange?: (theme: 'light' | 'dark' | 'system') => void
88
- userMenu?: {
89
- user?: {
90
- name?: string
91
- email?: string
92
- avatar?: string
93
- }
94
- items?: NavbarItem[]
95
- }
96
- notifications?: {
97
- count?: number
98
- items?: Array<{
99
- id: string
100
- title: string
101
- description?: string
102
- time?: string
103
- read?: boolean
104
- }>
105
- onNotificationClick?: (id: string) => void
106
- }
107
- cta?: {
108
- text: string
109
- href?: string
110
- action?: () => void
111
- variant?: 'primary' | 'secondary' | 'outline' | 'ghost'
112
- }
113
- sticky?: boolean
114
- glassmorphism?: boolean
115
- animatedBackground?: boolean
116
- keyboardShortcuts?: boolean
117
- mobileBreakpoint?: number
118
- customStyles?: {
119
- background?: string
120
- border?: string
121
- text?: string
122
- hover?: string
123
- }
124
- variant?: 'default' | 'floating' | 'minimal' | 'transparent'
125
- size?: 'sm' | 'md' | 'lg'
126
- blur?: boolean
127
- shadow?: boolean
128
- rounded?: boolean
129
- logoPosition?: 'left' | 'center'
130
- mobileMenuPosition?: 'left' | 'right'
131
- hideOnScroll?: boolean
132
- showScrollProgress?: boolean
133
- maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'
134
- }
135
-
136
- interface NavbarProps extends NavbarConfig {
137
- className?: string
138
- activePath?: string
139
- onNavigate?: (href: string) => void
140
- }
141
-
142
- export function Navbar({
143
- sections,
144
- branding,
145
- showSearch = true,
146
- searchPlaceholder = "Search...",
147
- onSearch,
148
- showThemeToggle = false,
149
- theme = 'system',
150
- onThemeChange,
151
- userMenu,
152
- notifications,
153
- cta,
154
- sticky = true,
155
- glassmorphism = false,
156
- animatedBackground = false,
157
- keyboardShortcuts = true,
158
- mobileBreakpoint = 768,
159
- customStyles,
160
- variant = 'default',
161
- size = 'md',
162
- blur = true,
163
- shadow = false,
164
- rounded = false,
165
- logoPosition = 'left',
166
- mobileMenuPosition = 'left',
167
- hideOnScroll = false,
168
- showScrollProgress = false,
169
- maxWidth = '2xl',
170
- className,
171
- activePath,
172
- onNavigate
173
- }: NavbarProps) {
174
- const [isMobile, setIsMobile] = useState(false)
175
- const [isOpen, setIsOpen] = useState(false)
176
- const [isSearchOpen, setIsSearchOpen] = useState(false)
177
- const [isCommandOpen, setIsCommandOpen] = useState(false)
178
- const [searchValue, setSearchValue] = useState('')
179
- const [scrolled, setScrolled] = useState(false)
180
- const [hidden, setHidden] = useState(false)
181
- const [scrollProgress, setScrollProgress] = useState(0)
182
- const lastScrollY = useRef(0)
183
- const navRef = useRef<HTMLElement>(null)
184
-
185
- // Check mobile
186
- useEffect(() => {
187
- const checkMobile = () => {
188
- setIsMobile(window.innerWidth < mobileBreakpoint)
189
- }
190
-
191
- checkMobile()
192
- window.addEventListener('resize', checkMobile)
193
- return () => window.removeEventListener('resize', checkMobile)
194
- }, [mobileBreakpoint])
195
-
196
- // Handle scroll for sticky navbar and hide on scroll
197
- useEffect(() => {
198
- if (!sticky && !hideOnScroll && !showScrollProgress) return
199
-
200
- const handleScroll = () => {
201
- const currentScrollY = window.scrollY
202
-
203
- // Set scrolled state
204
- setScrolled(currentScrollY > 10)
205
-
206
- // Hide on scroll
207
- if (hideOnScroll) {
208
- if (currentScrollY > lastScrollY.current && currentScrollY > 100) {
209
- setHidden(true)
210
- } else {
211
- setHidden(false)
212
- }
213
- lastScrollY.current = currentScrollY
214
- }
215
-
216
- // Scroll progress
217
- if (showScrollProgress) {
218
- const winScroll = window.scrollY
219
- const height = document.documentElement.scrollHeight - document.documentElement.clientHeight
220
- const scrolled = (winScroll / height) * 100
221
- setScrollProgress(scrolled)
222
- }
223
- }
224
-
225
- window.addEventListener('scroll', handleScroll, { passive: true })
226
- return () => window.removeEventListener('scroll', handleScroll)
227
- }, [sticky, hideOnScroll, showScrollProgress])
228
-
229
- // Keyboard shortcuts
230
- useEffect(() => {
231
- if (!keyboardShortcuts) return
232
-
233
- const handleKeyDown = (e: KeyboardEvent) => {
234
- // Cmd/Ctrl + K for search
235
- if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
236
- e.preventDefault()
237
- setIsCommandOpen(true)
238
- }
239
- }
240
-
241
- document.addEventListener('keydown', handleKeyDown)
242
- return () => document.removeEventListener('keydown', handleKeyDown)
243
- }, [keyboardShortcuts])
244
-
245
- const handleItemClick = useCallback((item: NavbarItem) => {
246
- if (item.action) {
247
- item.action()
248
- } else if (item.href && onNavigate) {
249
- onNavigate(item.href)
250
- if (isMobile) {
251
- setIsOpen(false)
252
- }
253
- }
254
- }, [onNavigate, isMobile])
255
-
256
- const handleSearch = useCallback((value: string) => {
257
- setSearchValue(value)
258
- onSearch?.(value)
259
- }, [onSearch])
260
-
261
- // Render mega menu content
262
- const renderMegaMenuContent = (items: NavbarItem[]) => (
263
- <div className="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
264
- {items.map(item => (
265
- <NavigationMenuLink
266
- key={item.id}
267
- className={cn(
268
- "block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
269
- item.disabled && "opacity-50 cursor-not-allowed"
270
- )}
271
- onClick={() => !item.disabled && handleItemClick(item)}
272
- >
273
- <div className="flex items-center gap-2">
274
- {item.icon && <span className="flex-shrink-0">{item.icon}</span>}
275
- <div className="text-sm font-medium leading-none">{item.title}</div>
276
- {item.badge && (
277
- <Badge variant={item.badgeVariant || 'secondary'} className="ml-auto">
278
- {item.badge}
279
- </Badge>
280
- )}
281
- </div>
282
- {item.description && (
283
- <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
284
- {item.description}
285
- </p>
286
- )}
287
- </NavigationMenuLink>
288
- ))}
289
- </div>
290
- )
291
-
292
- // Render dropdown menu
293
- const renderDropdownMenu = (items: NavbarItem[]) => (
294
- <>
295
- {items.map(item => {
296
- if (item.items && item.items.length > 0) {
297
- return (
298
- <DropdownMenuSub key={item.id}>
299
- <DropdownMenuSubTrigger disabled={item.disabled}>
300
- <span className="flex items-center gap-2">
301
- {item.icon && <span>{item.icon}</span>}
302
- {item.title}
303
- </span>
304
- </DropdownMenuSubTrigger>
305
- <DropdownMenuSubContent>
306
- {renderDropdownMenu(item.items)}
307
- </DropdownMenuSubContent>
308
- </DropdownMenuSub>
309
- )
310
- }
311
-
312
- return (
313
- <DropdownMenuItem
314
- key={item.id}
315
- disabled={item.disabled}
316
- onClick={() => handleItemClick(item)}
317
- >
318
- <span className="flex items-center gap-2">
319
- {item.icon && <span>{item.icon}</span>}
320
- {item.title}
321
- </span>
322
- {item.badge && (
323
- <Badge variant={item.badgeVariant || 'secondary'} className="ml-auto">
324
- {item.badge}
325
- </Badge>
326
- )}
327
- </DropdownMenuItem>
328
- )
329
- })}
330
- </>
331
- )
332
-
333
- // Mobile menu
334
- const MobileMenu = () => (
335
- <Sheet open={isOpen} onOpenChange={setIsOpen}>
336
- <SheetTrigger asChild>
337
- <Button variant="ghost" size="icon" className="md:hidden">
338
- <Menu className="h-5 w-5" />
339
- </Button>
340
- </SheetTrigger>
341
- <SheetContent side={mobileMenuPosition} className="w-80 p-0">
342
- <div className="flex h-full flex-col">
343
- <div className="flex items-center justify-between p-4 border-b">
344
- {branding && (
345
- <div className="flex items-center gap-2">
346
- {branding.logo}
347
- {branding.title && (
348
- <span className="font-semibold text-lg">{branding.title}</span>
349
- )}
350
- </div>
351
- )}
352
- <Button
353
- variant="ghost"
354
- size="icon"
355
- onClick={() => setIsOpen(false)}
356
- className="h-8 w-8"
357
- >
358
- <X className="h-4 w-4" />
359
- </Button>
360
- </div>
361
-
362
- <div className="flex-1 overflow-y-auto p-4">
363
- {sections.map(section => (
364
- <div key={section.id} className="space-y-1">
365
- {section.items.map(item => (
366
- <div key={item.id}>
367
- {item.items && item.items.length > 0 ? (
368
- <DropdownMenu>
369
- <DropdownMenuTrigger asChild>
370
- <Button
371
- variant="ghost"
372
- className="w-full justify-between"
373
- disabled={item.disabled}
374
- >
375
- <span className="flex items-center gap-2">
376
- {item.icon && <span>{item.icon}</span>}
377
- {item.title}
378
- </span>
379
- <ChevronDown className="h-4 w-4" />
380
- </Button>
381
- </DropdownMenuTrigger>
382
- <DropdownMenuContent align="start" className="w-56">
383
- {item.items && renderDropdownMenu(item.items)}
384
- </DropdownMenuContent>
385
- </DropdownMenu>
386
- ) : (
387
- <Button
388
- variant="ghost"
389
- className="w-full justify-start"
390
- disabled={item.disabled}
391
- onClick={() => handleItemClick(item)}
392
- >
393
- <span className="flex items-center gap-2">
394
- {item.icon && <span>{item.icon}</span>}
395
- {item.title}
396
- </span>
397
- {item.badge && (
398
- <Badge variant={item.badgeVariant || 'secondary'} className="ml-auto">
399
- {item.badge}
400
- </Badge>
401
- )}
402
- </Button>
403
- )}
404
- </div>
405
- ))}
406
- <Separator className="my-2" />
407
- </div>
408
- ))}
409
- </div>
410
- </div>
411
- </SheetContent>
412
- </Sheet>
413
- )
414
-
415
- // Get height based on size
416
- const getHeight = () => {
417
- switch (size) {
418
- case 'sm': return 'h-14'
419
- case 'lg': return 'h-20'
420
- default: return 'h-16'
421
- }
422
- }
423
-
424
- const navClasses = cn(
425
- "moonui-pro w-full transition-all duration-300",
426
- // Base variant styles
427
- variant === 'default' && "border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60",
428
- variant === 'floating' && "mx-4 mt-4 rounded-lg border bg-background/95 backdrop-blur shadow-lg",
429
- variant === 'minimal' && "bg-background",
430
- variant === 'transparent' && "bg-transparent",
431
- // Sticky behavior
432
- sticky && "sticky top-0 z-50",
433
- // Scroll states
434
- scrolled && sticky && variant !== 'transparent' && "shadow-sm",
435
- scrolled && variant === 'transparent' && "bg-background/95 backdrop-blur border-b",
436
- hidden && "-translate-y-full",
437
- // Glass effect
438
- glassmorphism && "bg-background/80 backdrop-blur-xl border-white/10",
439
- blur && "backdrop-blur-md",
440
- shadow && "shadow-lg",
441
- rounded && "rounded-lg",
442
- // Custom styles
443
- customStyles?.background,
444
- customStyles?.border,
445
- className
446
- )
447
-
448
- return (
449
- <>
450
- <nav ref={navRef} className={navClasses}>
451
- <div className={cn(
452
- "flex items-center",
453
- getHeight(),
454
- maxWidth === 'sm' && "container max-w-screen-sm",
455
- maxWidth === 'md' && "container max-w-screen-md",
456
- maxWidth === 'lg' && "container max-w-screen-lg",
457
- maxWidth === 'xl' && "container max-w-screen-xl",
458
- maxWidth === '2xl' && "container max-w-screen-2xl",
459
- maxWidth === 'full' && "w-full px-4 sm:px-6 lg:px-8"
460
- )}>
461
- {/* Branding */}
462
- {branding && (
463
- <div className={cn(
464
- "flex items-center gap-2",
465
- logoPosition === 'center' && !isMobile ? "absolute left-1/2 -translate-x-1/2" : "mr-4"
466
- )}>
467
- {logoPosition === 'left' && mobileMenuPosition === 'left' && <MobileMenu />}
468
- <a
469
- href={branding.href || '/'}
470
- onClick={(e) => {
471
- if (branding.href && onNavigate) {
472
- e.preventDefault()
473
- onNavigate(branding.href)
474
- }
475
- }}
476
- className="flex items-center gap-2"
477
- >
478
- {branding.logo}
479
- {branding.title && !isMobile && (
480
- <span className="font-semibold text-lg">{branding.title}</span>
481
- )}
482
- </a>
483
- </div>
484
- )}
485
-
486
- {/* Desktop Navigation */}
487
- {!isMobile && (
488
- <NavigationMenu className="hidden md:flex">
489
- <NavigationMenuList>
490
- {sections.map(section => (
491
- <React.Fragment key={section.id}>
492
- {section.items.map(item => {
493
- const hasChildren = item.items && item.items.length > 0
494
- const isActive = item.href === activePath
495
-
496
- if (hasChildren) {
497
- return (
498
- <NavigationMenuItem key={item.id}>
499
- <NavigationMenuTrigger
500
- className={cn(
501
- isActive && "bg-accent text-accent-foreground",
502
- item.disabled && "opacity-50 cursor-not-allowed"
503
- )}
504
- disabled={item.disabled}
505
- >
506
- <span className="flex items-center gap-2">
507
- {item.icon && <span>{item.icon}</span>}
508
- {item.title}
509
- </span>
510
- </NavigationMenuTrigger>
511
- <NavigationMenuContent>
512
- {item.items && renderMegaMenuContent(item.items)}
513
- </NavigationMenuContent>
514
- </NavigationMenuItem>
515
- )
516
- }
517
-
518
- return (
519
- <NavigationMenuItem key={item.id}>
520
- <NavigationMenuLink
521
- className={cn(
522
- navigationMenuTriggerStyle(),
523
- isActive && "bg-accent text-accent-foreground",
524
- item.disabled && "opacity-50 cursor-not-allowed"
525
- )}
526
- onClick={() => !item.disabled && handleItemClick(item)}
527
- >
528
- <span className="flex items-center gap-2">
529
- {item.icon && <span>{item.icon}</span>}
530
- {item.title}
531
- </span>
532
- {item.badge && (
533
- <Badge variant={item.badgeVariant || 'secondary'} className="ml-2">
534
- {item.badge}
535
- </Badge>
536
- )}
537
- </NavigationMenuLink>
538
- </NavigationMenuItem>
539
- )
540
- })}
541
- </React.Fragment>
542
- ))}
543
- </NavigationMenuList>
544
- </NavigationMenu>
545
- )}
546
-
547
- {/* Right side actions */}
548
- <div className="ml-auto flex items-center gap-2">
549
- {/* Search */}
550
- {showSearch && (
551
- <>
552
- {isMobile ? (
553
- <Button
554
- variant="ghost"
555
- size="icon"
556
- onClick={() => setIsCommandOpen(true)}
557
- >
558
- <Search className="h-5 w-5" />
559
- </Button>
560
- ) : (
561
- <div className="relative">
562
- <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
563
- <Input
564
- type="search"
565
- placeholder={searchPlaceholder}
566
- className="w-64 pl-9 pr-4 h-9"
567
- value={searchValue}
568
- onChange={(e) => handleSearch(e.target.value)}
569
- />
570
- {keyboardShortcuts && (
571
- <kbd className="absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 hidden sm:flex">
572
- <span className="text-xs">⌘</span>K
573
- </kbd>
574
- )}
575
- </div>
576
- )}
577
- </>
578
- )}
579
-
580
- {/* Theme Toggle */}
581
- {showThemeToggle && (
582
- <DropdownMenu>
583
- <DropdownMenuTrigger asChild>
584
- <Button variant="ghost" size="icon">
585
- {theme === 'light' && <Sun className="h-5 w-5" />}
586
- {theme === 'dark' && <Moon className="h-5 w-5" />}
587
- {theme === 'system' && <Monitor className="h-5 w-5" />}
588
- </Button>
589
- </DropdownMenuTrigger>
590
- <DropdownMenuContent align="end">
591
- <DropdownMenuItem onClick={() => onThemeChange?.('light')}>
592
- <Sun className="mr-2 h-4 w-4" />
593
- Light
594
- </DropdownMenuItem>
595
- <DropdownMenuItem onClick={() => onThemeChange?.('dark')}>
596
- <Moon className="mr-2 h-4 w-4" />
597
- Dark
598
- </DropdownMenuItem>
599
- <DropdownMenuItem onClick={() => onThemeChange?.('system')}>
600
- <Monitor className="mr-2 h-4 w-4" />
601
- System
602
- </DropdownMenuItem>
603
- </DropdownMenuContent>
604
- </DropdownMenu>
605
- )}
606
-
607
- {/* Notifications */}
608
- {notifications && (
609
- <DropdownMenu>
610
- <DropdownMenuTrigger asChild>
611
- <Button variant="ghost" size="icon" className="relative">
612
- <Bell className="h-5 w-5" />
613
- {notifications.count && notifications.count > 0 && (
614
- <span className="absolute -top-1 -right-1 h-4 w-4 rounded-full bg-destructive text-[10px] font-medium text-destructive-foreground flex items-center justify-center">
615
- {notifications.count}
616
- </span>
617
- )}
618
- </Button>
619
- </DropdownMenuTrigger>
620
- <DropdownMenuContent align="end" className="w-80">
621
- <div className="flex items-center justify-between p-4">
622
- <h4 className="text-sm font-semibold">Notifications</h4>
623
- {notifications.count && notifications.count > 0 && (
624
- <Badge variant="secondary">{notifications.count} new</Badge>
625
- )}
626
- </div>
627
- <DropdownMenuSeparator />
628
- <div className="max-h-[300px] overflow-y-auto">
629
- {notifications.items && notifications.items.length > 0 ? (
630
- notifications.items.map(notification => (
631
- <DropdownMenuItem
632
- key={notification.id}
633
- className="flex flex-col items-start p-4 cursor-pointer"
634
- onClick={() => notifications.onNotificationClick?.(notification.id)}
635
- >
636
- <div className="flex w-full items-start justify-between">
637
- <p className={cn(
638
- "text-sm font-medium",
639
- !notification.read && "text-primary"
640
- )}>
641
- {notification.title}
642
- </p>
643
- {!notification.read && (
644
- <span className="h-2 w-2 rounded-full bg-primary" />
645
- )}
646
- </div>
647
- {notification.description && (
648
- <p className="text-xs text-muted-foreground mt-1">
649
- {notification.description}
650
- </p>
651
- )}
652
- {notification.time && (
653
- <p className="text-xs text-muted-foreground mt-2">
654
- {notification.time}
655
- </p>
656
- )}
657
- </DropdownMenuItem>
658
- ))
659
- ) : (
660
- <div className="p-4 text-center text-sm text-muted-foreground">
661
- No notifications
662
- </div>
663
- )}
664
- </div>
665
- </DropdownMenuContent>
666
- </DropdownMenu>
667
- )}
668
-
669
- {/* CTA Button */}
670
- {cta && (
671
- <Button
672
- variant={cta.variant || 'primary'}
673
- size="sm"
674
- onClick={() => {
675
- if (cta.action) {
676
- cta.action()
677
- } else if (cta.href && onNavigate) {
678
- onNavigate(cta.href)
679
- }
680
- }}
681
- className={!isMobile ? 'ml-2' : ''}
682
- >
683
- {cta.text}
684
- </Button>
685
- )}
686
-
687
- {/* User Menu */}
688
- {userMenu && (
689
- <DropdownMenu>
690
- <DropdownMenuTrigger asChild>
691
- <Button variant="ghost" size="icon" className="relative">
692
- <Avatar className="h-8 w-8">
693
- <AvatarImage src={userMenu.user?.avatar} alt={userMenu.user?.name} />
694
- <AvatarFallback>
695
- {userMenu.user?.name?.charAt(0) || <User className="h-4 w-4" />}
696
- </AvatarFallback>
697
- </Avatar>
698
- </Button>
699
- </DropdownMenuTrigger>
700
- <DropdownMenuContent align="end" className="w-56">
701
- {userMenu.user && (
702
- <>
703
- <div className="p-2">
704
- <p className="text-sm font-medium">{userMenu.user.name}</p>
705
- <p className="text-xs text-muted-foreground">{userMenu.user.email}</p>
706
- </div>
707
- <DropdownMenuSeparator />
708
- </>
709
- )}
710
- {userMenu.items && renderDropdownMenu(userMenu.items)}
711
- </DropdownMenuContent>
712
- </DropdownMenu>
713
- )}
714
- </div>
715
- </div>
716
-
717
- {/* Animated background */}
718
- {animatedBackground && (
719
- <motion.div
720
- className="absolute inset-0 -z-10 opacity-30"
721
- animate={{
722
- background: [
723
- 'radial-gradient(circle at 20% 50%, rgba(120, 119, 198, 0.3), transparent 50%)',
724
- 'radial-gradient(circle at 80% 50%, rgba(120, 119, 198, 0.3), transparent 50%)',
725
- 'radial-gradient(circle at 20% 50%, rgba(120, 119, 198, 0.3), transparent 50%)',
726
- ],
727
- }}
728
- transition={{
729
- duration: 10,
730
- repeat: Infinity,
731
- ease: "linear"
732
- }}
733
- />
734
- )}
735
-
736
- {/* Scroll Progress Bar */}
737
- {showScrollProgress && (
738
- <div className="absolute bottom-0 left-0 w-full h-0.5 bg-muted">
739
- <div
740
- className="h-full bg-primary transition-all duration-150"
741
- style={{ width: `${scrollProgress}%` }}
742
- />
743
- </div>
744
- )}
745
- </nav>
746
-
747
- {/* Command Dialog for search */}
748
- <CommandDialog open={isCommandOpen} onOpenChange={setIsCommandOpen}>
749
- <CommandInput
750
- placeholder={searchPlaceholder}
751
- value={searchValue}
752
- onValueChange={handleSearch}
753
- />
754
- <CommandList>
755
- <CommandEmpty>No results found.</CommandEmpty>
756
- {sections.map(section => (
757
- <CommandGroup key={section.id}>
758
- {section.items.map(item => (
759
- <CommandItem
760
- key={item.id}
761
- disabled={item.disabled}
762
- onSelect={() => {
763
- handleItemClick(item)
764
- setIsCommandOpen(false)
765
- }}
766
- >
767
- <span className="flex items-center gap-2">
768
- {item.icon && <span>{item.icon}</span>}
769
- {item.title}
770
- </span>
771
- </CommandItem>
772
- ))}
773
- </CommandGroup>
774
- ))}
775
- </CommandList>
776
- </CommandDialog>
777
- </>
778
- )
779
- }
780
-
781
- export default Navbar