@moontra/moonui-pro 2.20.0 → 2.20.2

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 (76) hide show
  1. package/dist/index.d.ts +691 -261
  2. package/dist/index.mjs +7419 -4935
  3. package/package.json +4 -3
  4. package/scripts/postbuild.js +27 -0
  5. package/src/components/advanced-chart/index.tsx +5 -1
  6. package/src/components/advanced-forms/index.tsx +175 -16
  7. package/src/components/calendar/event-dialog.tsx +18 -13
  8. package/src/components/calendar/index.tsx +197 -50
  9. package/src/components/dashboard/dashboard-grid.tsx +21 -3
  10. package/src/components/dashboard/types.ts +3 -0
  11. package/src/components/dashboard/widgets/activity-feed.tsx +6 -1
  12. package/src/components/dashboard/widgets/comparison-widget.tsx +177 -0
  13. package/src/components/dashboard/widgets/index.ts +5 -0
  14. package/src/components/dashboard/widgets/metric-card.tsx +21 -1
  15. package/src/components/dashboard/widgets/progress-widget.tsx +113 -0
  16. package/src/components/error-boundary/index.tsx +160 -37
  17. package/src/components/form-wizard/form-wizard-context.tsx +54 -26
  18. package/src/components/form-wizard/form-wizard-progress.tsx +33 -2
  19. package/src/components/form-wizard/types.ts +2 -1
  20. package/src/components/github-stars/hooks.ts +1 -0
  21. package/src/components/github-stars/variants.tsx +3 -1
  22. package/src/components/health-check/index.tsx +14 -14
  23. package/src/components/hover-card-3d/index.tsx +2 -3
  24. package/src/components/index.ts +5 -3
  25. package/src/components/kanban/kanban.tsx +23 -18
  26. package/src/components/license-error/index.tsx +2 -0
  27. package/src/components/magnetic-button/index.tsx +56 -7
  28. package/src/components/memory-efficient-data/index.tsx +117 -115
  29. package/src/components/navbar/index.tsx +781 -0
  30. package/src/components/performance-debugger/index.tsx +62 -38
  31. package/src/components/performance-monitor/index.tsx +47 -33
  32. package/src/components/phone-number-input/index.tsx +32 -27
  33. package/src/components/phone-number-input/phone-number-input-simple.tsx +167 -0
  34. package/src/components/rich-text-editor/index.tsx +26 -28
  35. package/src/components/rich-text-editor/slash-commands-extension.ts +15 -5
  36. package/src/components/sidebar/index.tsx +32 -13
  37. package/src/components/timeline/index.tsx +84 -49
  38. package/src/components/ui/accordion.tsx +550 -42
  39. package/src/components/ui/avatar.tsx +2 -0
  40. package/src/components/ui/badge.tsx +2 -0
  41. package/src/components/ui/breadcrumb.tsx +2 -0
  42. package/src/components/ui/button.tsx +39 -33
  43. package/src/components/ui/card.tsx +2 -0
  44. package/src/components/ui/collapsible.tsx +546 -50
  45. package/src/components/ui/command.tsx +790 -67
  46. package/src/components/ui/dialog.tsx +510 -92
  47. package/src/components/ui/dropdown-menu.tsx +540 -52
  48. package/src/components/ui/index.ts +37 -5
  49. package/src/components/ui/input.tsx +2 -0
  50. package/src/components/ui/magnetic-button.tsx +1 -1
  51. package/src/components/ui/media-gallery.tsx +1 -2
  52. package/src/components/ui/navigation-menu.tsx +130 -0
  53. package/src/components/ui/pagination.tsx +2 -0
  54. package/src/components/ui/select.tsx +6 -2
  55. package/src/components/ui/spotlight-card.tsx +1 -1
  56. package/src/components/ui/table.tsx +2 -0
  57. package/src/components/ui/tabs-pro.tsx +542 -0
  58. package/src/components/ui/tabs.tsx +23 -167
  59. package/src/components/ui/toggle.tsx +13 -13
  60. package/src/index.ts +11 -3
  61. package/src/styles/index.css +596 -0
  62. package/src/use-performance-optimizer.ts +1 -1
  63. package/src/utils/chart-helpers.ts +1 -1
  64. package/src/__tests__/use-intersection-observer.test.tsx +0 -216
  65. package/src/__tests__/use-local-storage.test.tsx +0 -174
  66. package/src/__tests__/use-pro-access.test.tsx +0 -183
  67. package/src/components/advanced-chart/advanced-chart.test.tsx +0 -281
  68. package/src/components/data-table/data-table.test.tsx +0 -187
  69. package/src/components/enhanced/badge.tsx +0 -191
  70. package/src/components/enhanced/button.tsx +0 -362
  71. package/src/components/enhanced/card.tsx +0 -266
  72. package/src/components/enhanced/dialog.tsx +0 -246
  73. package/src/components/enhanced/index.ts +0 -4
  74. package/src/components/file-upload/file-upload.test.tsx +0 -243
  75. package/src/components/rich-text-editor/index-old-backup.tsx +0 -437
  76. package/src/types/moonui.d.ts +0 -22
@@ -21,7 +21,7 @@ import TaskItem from '@tiptap/extension-task-item';
21
21
  import Typography from '@tiptap/extension-typography';
22
22
  import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';
23
23
  import { common, createLowlight } from 'lowlight';
24
- import { SlashCommandsExtension } from './slash-commands-extension';
24
+ import { SlashCommandsExtension, type SlashCommand } from './slash-commands-extension';
25
25
  import { motion } from 'framer-motion';
26
26
 
27
27
  // Import pro access hooks
@@ -138,14 +138,7 @@ const SUPPORTED_LANGUAGES = [
138
138
  { code: 'nl', name: 'Dutch', nativeName: 'Nederlands' },
139
139
  ];
140
140
 
141
- interface SlashCommand {
142
- id?: string
143
- title?: string
144
- description: string
145
- command: string
146
- icon?: string
147
- action: (text: string) => Promise<{ text: string; error?: string }> | void
148
- }
141
+ // Use SlashCommand from slash-commands-extension.ts
149
142
 
150
143
  // Get AI provider instance
151
144
  const getAIProvider = (settings: AISettingsType): AIProviderInterface | null => {
@@ -177,7 +170,7 @@ const getAIProvider = (settings: AISettingsType): AIProviderInterface | null =>
177
170
  import './slash-commands.css';
178
171
  import './table-styles.css';
179
172
 
180
- export interface AIConfig {
173
+ export interface EditorAIConfig {
181
174
  provider?: 'openai' | 'claude' | 'gemini' | 'cohere';
182
175
  apiKey?: string;
183
176
  model?: string;
@@ -208,7 +201,7 @@ interface RichTextEditorProps {
208
201
  color?: boolean;
209
202
  ai?: boolean;
210
203
  };
211
- aiConfig?: AIConfig;
204
+ aiConfig?: EditorAIConfig;
212
205
  persistAISettings?: boolean;
213
206
  }
214
207
 
@@ -392,10 +385,8 @@ export function RichTextEditor({
392
385
  if (persistAISettings && typeof window !== 'undefined') {
393
386
  try {
394
387
  const stored = localStorage.getItem('moonui-ai-settings');
395
- console.log('[RichTextEditor] Loading AI settings from localStorage:', stored);
396
388
  if (stored) {
397
389
  const parsed = JSON.parse(stored);
398
- console.log('[RichTextEditor] Parsed AI settings:', parsed);
399
390
  // Props'tan gelen değerler her zaman öncelikli
400
391
  const settings = {
401
392
  provider: (aiConfig.provider !== undefined ? aiConfig.provider : parsed.provider || 'openai') as 'openai' | 'claude' | 'gemini' | 'cohere',
@@ -404,7 +395,6 @@ export function RichTextEditor({
404
395
  temperature: aiConfig.temperature !== undefined ? aiConfig.temperature : parsed.temperature ?? 0.7,
405
396
  maxTokens: aiConfig.maxTokens !== undefined ? aiConfig.maxTokens : parsed.maxTokens ?? 1000,
406
397
  };
407
- console.log('[RichTextEditor] Final settings with props override:', settings);
408
398
  return settings;
409
399
  }
410
400
  } catch (e) {
@@ -420,7 +410,6 @@ export function RichTextEditor({
420
410
  temperature: aiConfig.temperature ?? 0.7,
421
411
  maxTokens: aiConfig.maxTokens ?? 1000,
422
412
  };
423
- console.log('[RichTextEditor] Using default settings:', defaultSettings);
424
413
  return defaultSettings;
425
414
  });
426
415
  const [isAiSettingsOpen, setIsAiSettingsOpen] = useState(false);
@@ -714,16 +703,16 @@ export function RichTextEditor({
714
703
  const selectedText = editor.state.doc.textBetween(from, to, ' ');
715
704
 
716
705
  setIsProcessing(true);
717
- setCurrentAction(command.id);
706
+ setCurrentAction(command.id || '');
718
707
 
719
708
  try {
720
- const response = await command.action(selectedText || editor.getText());
709
+ const response = command.action ? await command.action(selectedText || editor.getText()) : { text: '', error: 'No action defined' };
721
710
  if (response.text) {
722
711
  // Check if this command should use modal preview
723
- if (shouldUseModal(command.id)) {
712
+ if (shouldUseModal(command.id || '')) {
724
713
  // Open preview modal
725
714
  setPreviewContent(response.text);
726
- setPreviewAction(command.id);
715
+ setPreviewAction(command.id || '');
727
716
  setPreviewOriginalText(selectedText || editor.getText());
728
717
  setIsAiPreviewOpen(true);
729
718
  } else {
@@ -1762,7 +1751,7 @@ export function RichTextEditor({
1762
1751
  <Settings className="w-4 h-4" />
1763
1752
  </ToolbarButton>
1764
1753
  </DialogTrigger>
1765
- <DialogContent className="sm:max-w-[425px]">
1754
+ <DialogContent className="sm:max-w-[425px] overflow-visible" style={{ zIndex: 9998 }}>
1766
1755
  <DialogHeader>
1767
1756
  <DialogTitle>AI Settings</DialogTitle>
1768
1757
  <DialogDescription>
@@ -1775,11 +1764,12 @@ export function RichTextEditor({
1775
1764
  <Select
1776
1765
  value={aiSettings.provider}
1777
1766
  onValueChange={(value: 'openai' | 'claude' | 'gemini' | 'cohere') => {
1767
+ console.log('Provider changed to:', value);
1778
1768
  // Update model when provider changes
1779
1769
  const defaultModels = {
1780
1770
  openai: 'gpt-3.5-turbo',
1781
1771
  claude: 'claude-3-sonnet-20240229',
1782
- gemini: 'gemini-2.0-flash',
1772
+ gemini: 'gemini-2.0-flash-exp',
1783
1773
  cohere: 'command'
1784
1774
  };
1785
1775
  const newSettings = {
@@ -1797,7 +1787,7 @@ export function RichTextEditor({
1797
1787
  <SelectTrigger>
1798
1788
  <SelectValue />
1799
1789
  </SelectTrigger>
1800
- <SelectContent>
1790
+ <SelectContent className="z-[9999]" sideOffset={5}>
1801
1791
  <SelectItem value="openai">OpenAI</SelectItem>
1802
1792
  <SelectItem value="claude">Claude (Anthropic)</SelectItem>
1803
1793
  <SelectItem value="gemini">Gemini (Google)</SelectItem>
@@ -1838,25 +1828,33 @@ export function RichTextEditor({
1838
1828
  <SelectTrigger>
1839
1829
  <SelectValue />
1840
1830
  </SelectTrigger>
1841
- <SelectContent>
1831
+ <SelectContent className="z-[9999]" sideOffset={5}>
1842
1832
  {aiSettings.provider === 'openai' && (
1843
1833
  <>
1834
+ <SelectItem value="gpt-4-turbo-preview">GPT-4 Turbo</SelectItem>
1844
1835
  <SelectItem value="gpt-4">GPT-4</SelectItem>
1845
1836
  <SelectItem value="gpt-3.5-turbo">GPT-3.5 Turbo</SelectItem>
1837
+ <SelectItem value="gpt-3.5-turbo-16k">GPT-3.5 Turbo 16K</SelectItem>
1846
1838
  </>
1847
1839
  )}
1848
1840
  {aiSettings.provider === 'claude' && (
1849
1841
  <>
1850
- <SelectItem value="claude-3-opus">Claude 3 Opus</SelectItem>
1851
- <SelectItem value="claude-3-sonnet">Claude 3 Sonnet</SelectItem>
1852
- <SelectItem value="claude-3-haiku">Claude 3 Haiku</SelectItem>
1842
+ <SelectItem value="claude-3-opus-20240229">Claude 3 Opus</SelectItem>
1843
+ <SelectItem value="claude-3-sonnet-20240229">Claude 3 Sonnet</SelectItem>
1844
+ <SelectItem value="claude-3-haiku-20240307">Claude 3 Haiku</SelectItem>
1845
+ <SelectItem value="claude-2.1">Claude 2.1</SelectItem>
1846
+ <SelectItem value="claude-2.0">Claude 2.0</SelectItem>
1853
1847
  </>
1854
1848
  )}
1855
1849
  {aiSettings.provider === 'gemini' && (
1856
1850
  <>
1857
- <SelectItem value="gemini-2.0-flash">Gemini 2.0 Flash</SelectItem>
1858
- <SelectItem value="gemini-1.5-flash">Gemini 1.5 Flash</SelectItem>
1851
+ <SelectItem value="gemini-2.0-flash-exp">Gemini 2.0 Flash (Experimental)</SelectItem>
1852
+ <SelectItem value="gemini-2.0-flash-thinking-exp">Gemini 2.0 Flash Thinking (Experimental)</SelectItem>
1859
1853
  <SelectItem value="gemini-1.5-pro">Gemini 1.5 Pro</SelectItem>
1854
+ <SelectItem value="gemini-1.5-flash">Gemini 1.5 Flash</SelectItem>
1855
+ <SelectItem value="gemini-1.5-flash-8b">Gemini 1.5 Flash 8B</SelectItem>
1856
+ <SelectItem value="gemini-pro">Gemini Pro</SelectItem>
1857
+ <SelectItem value="gemini-pro-vision">Gemini Pro Vision</SelectItem>
1860
1858
  </>
1861
1859
  )}
1862
1860
  {aiSettings.provider === 'cohere' && (
@@ -1,7 +1,15 @@
1
1
  import { Extension } from '@tiptap/core';
2
2
  import { Plugin, PluginKey } from '@tiptap/pm/state';
3
3
  import { Decoration, DecorationSet } from '@tiptap/pm/view';
4
- import type { SlashCommand } from '@/lib/ai-providers';
4
+
5
+ // SlashCommand type definition
6
+ export interface SlashCommand {
7
+ id?: string;
8
+ command: string;
9
+ description: string;
10
+ icon?: string;
11
+ action?: (text: string) => Promise<{ text?: string; error?: string }>;
12
+ }
5
13
 
6
14
  declare module '@tiptap/core' {
7
15
  interface Commands<ReturnType> {
@@ -29,6 +37,7 @@ export const SlashCommandsExtension = Extension.create<SlashCommandsOptions>({
29
37
  },
30
38
 
31
39
  addCommands() {
40
+ const extension = this;
32
41
  return {
33
42
  selectSlashCommand: (index: number) => ({ editor }) => {
34
43
  const state = slashCommandsPluginKey.getState(editor.state);
@@ -43,7 +52,7 @@ export const SlashCommandsExtension = Extension.create<SlashCommandsOptions>({
43
52
  .run();
44
53
 
45
54
  // Execute the command
46
- this.options.onSelectCommand(command);
55
+ extension.options.onSelectCommand(command);
47
56
 
48
57
  return true;
49
58
  }
@@ -98,6 +107,7 @@ export const SlashCommandsExtension = Extension.create<SlashCommandsOptions>({
98
107
  },
99
108
 
100
109
  addProseMirrorPlugins() {
110
+ const extension = this;
101
111
  return [
102
112
  new Plugin({
103
113
  key: slashCommandsPluginKey,
@@ -136,7 +146,7 @@ export const SlashCommandsExtension = Extension.create<SlashCommandsOptions>({
136
146
 
137
147
  if (match) {
138
148
  const query = match[1].toLowerCase();
139
- const filteredCommands = this.options.commands.filter(cmd =>
149
+ const filteredCommands = extension.options.commands.filter((cmd: SlashCommand) =>
140
150
  cmd.command.toLowerCase().includes(query) ||
141
151
  cmd.description.toLowerCase().includes(query)
142
152
  );
@@ -159,7 +169,7 @@ export const SlashCommandsExtension = Extension.create<SlashCommandsOptions>({
159
169
  props: {
160
170
  decorations(state) {
161
171
  const pluginState = this.getState(state);
162
- if (!pluginState.active) {
172
+ if (!pluginState || !pluginState.active) {
163
173
  return DecorationSet.empty;
164
174
  }
165
175
 
@@ -169,7 +179,7 @@ export const SlashCommandsExtension = Extension.create<SlashCommandsOptions>({
169
179
  const container = document.createElement('div');
170
180
  container.className = 'slash-commands-menu';
171
181
 
172
- pluginState.filteredCommands.forEach((cmd, index) => {
182
+ pluginState.filteredCommands.forEach((cmd: SlashCommand, index: number) => {
173
183
  const item = document.createElement('div');
174
184
  item.className = `slash-command-item ${index === pluginState.selectedIndex ? 'selected' : ''}`;
175
185
 
@@ -71,11 +71,15 @@ const SearchInput = React.memo(({
71
71
  clearTimeout(timeoutRef.current)
72
72
  }
73
73
 
74
- // Debounce the parent update
74
+ // Debounce the parent update with shorter delay
75
75
  timeoutRef.current = setTimeout(() => {
76
76
  onSearchChange(newValue)
77
- }, 300)
78
- }, [onSearchChange])
77
+ // Keep focus on input after state update
78
+ if (searchInputRef.current) {
79
+ searchInputRef.current.focus()
80
+ }
81
+ }, 150)
82
+ }, [onSearchChange, searchInputRef])
79
83
 
80
84
  // Cleanup timeout on unmount
81
85
  useEffect(() => {
@@ -112,6 +116,8 @@ const SearchInput = React.memo(({
112
116
  )
113
117
  })
114
118
 
119
+ SearchInput.displayName = 'SearchInput'
120
+
115
121
  export interface SidebarItem {
116
122
  id: string
117
123
  title: string
@@ -351,6 +357,7 @@ export function Sidebar({
351
357
 
352
358
  // Handle search change
353
359
  const handleSearch = useCallback((value: string) => {
360
+ setCurrentSearchQuery(value)
354
361
  onSearchChange?.(value)
355
362
  }, [onSearchChange])
356
363
 
@@ -780,16 +787,6 @@ export function Sidebar({
780
787
  const SidebarContent = React.memo(() => {
781
788
  return (
782
789
  <>
783
- <SidebarHeader />
784
- {showSearch && (!collapsed || isMobile) && (
785
- <SearchInput
786
- searchInputRef={searchInputRef}
787
- searchPlaceholder={searchPlaceholder}
788
- initialValue={searchQuery}
789
- onSearchChange={handleSearch}
790
- keyboardShortcuts={keyboardShortcuts}
791
- />
792
- )}
793
790
  <SidebarMenuContent />
794
791
  <SidebarFooter />
795
792
  </>
@@ -839,6 +836,17 @@ export function Sidebar({
839
836
  }}
840
837
  />
841
838
  )}
839
+ <SidebarHeader />
840
+ {showSearch && (!collapsed || isMobile) && (
841
+ <SearchInput
842
+ key="sidebar-search-mobile"
843
+ searchInputRef={searchInputRef}
844
+ searchPlaceholder={searchPlaceholder}
845
+ initialValue={currentSearchQuery}
846
+ onSearchChange={handleSearch}
847
+ keyboardShortcuts={keyboardShortcuts}
848
+ />
849
+ )}
842
850
  <SidebarContent />
843
851
  </div>
844
852
  </SheetContent>
@@ -857,6 +865,17 @@ export function Sidebar({
857
865
  }}
858
866
  />
859
867
  )}
868
+ <SidebarHeader />
869
+ {showSearch && (!collapsed || isMobile) && (
870
+ <SearchInput
871
+ key="sidebar-search-desktop"
872
+ searchInputRef={searchInputRef}
873
+ searchPlaceholder={searchPlaceholder}
874
+ initialValue={currentSearchQuery}
875
+ onSearchChange={handleSearch}
876
+ keyboardShortcuts={keyboardShortcuts}
877
+ />
878
+ )}
860
879
  <SidebarContent />
861
880
  </aside>
862
881
  )
@@ -203,23 +203,23 @@ const timelineVariants = cva("w-full", {
203
203
 
204
204
  // Default color schemes
205
205
  const DEFAULT_COLORS: Record<TimelineEventType, string> = {
206
- success: 'bg-green-500 border-green-500',
207
- warning: 'bg-yellow-500 border-yellow-500',
208
- error: 'bg-red-500 border-red-500',
209
- info: 'bg-blue-500 border-blue-500',
210
- pending: 'bg-muted-foreground/40 border-muted-foreground/40',
211
- milestone: 'bg-purple-500 border-purple-500',
212
- custom: 'bg-slate-500 border-slate-500'
206
+ success: 'bg-green-500 border-green-600',
207
+ warning: 'bg-yellow-500 border-yellow-600',
208
+ error: 'bg-red-500 border-red-600',
209
+ info: 'bg-blue-500 border-blue-600',
210
+ pending: 'bg-gray-400 border-gray-500',
211
+ milestone: 'bg-purple-500 border-purple-600',
212
+ custom: 'bg-slate-500 border-slate-600'
213
213
  }
214
214
 
215
215
  const DEFAULT_ICONS: Record<TimelineEventType, React.ReactNode> = {
216
- success: <CheckCircle2 className="h-4 w-4 text-white" />,
217
- warning: <AlertCircle className="h-4 w-4 text-white" />,
218
- error: <XCircle className="h-4 w-4 text-white" />,
219
- info: <Circle className="h-4 w-4 text-white" />,
220
- pending: <Clock className="h-4 w-4 text-white" />,
221
- milestone: <Flag className="h-4 w-4 text-white" />,
222
- custom: <Sparkles className="h-4 w-4 text-white" />
216
+ success: <CheckCircle2 className="h-5 w-5 text-white" />,
217
+ warning: <AlertCircle className="h-5 w-5 text-white" />,
218
+ error: <XCircle className="h-5 w-5 text-white" />,
219
+ info: <Circle className="h-5 w-5 text-white" />,
220
+ pending: <Clock className="h-5 w-5 text-white" />,
221
+ milestone: <Flag className="h-5 w-5 text-white" />,
222
+ custom: <Sparkles className="h-5 w-5 text-white" />
223
223
  }
224
224
 
225
225
  const TEXT_COLORS: Record<TimelineEventType, string> = {
@@ -540,25 +540,36 @@ export function Timeline({
540
540
 
541
541
  if (isLast) return null
542
542
 
543
- const baseClasses = cn(
544
- "absolute",
545
- layout === 'horizontal' ? "h-0.5 top-4" : "w-0.5 left-1/2 -ml-px transform -translate-x-1/2",
546
- gradientConnectors && "bg-gradient-to-b from-border to-transparent"
547
- )
548
-
549
543
  if (layout === 'horizontal') {
550
- return <div className={cn(baseClasses, "bg-border")} style={{ width: '100px' }} />
544
+ return (
545
+ <div
546
+ className={cn(
547
+ "absolute h-1 bg-border",
548
+ gradientConnectors ? "bg-gradient-to-r from-primary/60 via-primary/30 to-transparent" : "bg-border"
549
+ )}
550
+ style={{
551
+ width: '100%',
552
+ left: '100%',
553
+ top: '50%',
554
+ transform: 'translateY(-50%)'
555
+ }}
556
+ />
557
+ )
551
558
  }
552
559
 
560
+ const baseClasses = cn(
561
+ "absolute w-1 left-1/2 -ml-0.5 transform -translate-x-1/2",
562
+ gradientConnectors ? "bg-gradient-to-b from-primary/60 via-primary/30 to-transparent" : "bg-border"
563
+ )
564
+
553
565
  return (
554
566
  <div className={cn(
555
567
  baseClasses,
556
- "bg-border",
557
- layout === 'alternating' && "hidden"
568
+ layout === 'alternating' && "left-1/2 -ml-0.5"
558
569
  )}
559
570
  style={{
560
- height: 'calc(100% + 2rem)',
561
- top: '2rem'
571
+ height: 'calc(100% + 3rem)',
572
+ top: '2.5rem'
562
573
  }} />
563
574
  )
564
575
  }, [connectorRenderer, layout, gradientConnectors])
@@ -587,17 +598,14 @@ export function Timeline({
587
598
  }}
588
599
  tabIndex={keyboardNavigation ? 0 : -1}
589
600
  className={cn(
590
- "relative flex gap-4",
591
- compactMode ? "pb-2" : "pb-8",
601
+ "relative flex gap-6",
602
+ compactMode ? "pb-4" : "pb-10",
592
603
  layout === 'alternating' && index % 2 === 0 && "flex-row-reverse",
593
- layout === 'horizontal' && "flex-col items-center",
594
- "cursor-pointer rounded-lg p-3 -m-1 transition-all duration-200",
595
- "hover:bg-muted/50 focus:outline-none focus:ring-2 focus:ring-ring",
596
- isSelected && "bg-muted/70",
597
- isFocused && "ring-2 ring-ring",
604
+ layout === 'horizontal' && "flex-col items-center pb-0 mr-8",
605
+ "transition-all duration-200",
606
+ isFocused && "scale-[1.02]",
598
607
  printMode && "print:break-inside-avoid"
599
608
  )}
600
- onClick={() => handleEventClick(event)}
601
609
  onFocus={() => setFocusedEventId(event.id)}
602
610
  role="button"
603
611
  aria-label={`Event: ${event.title}`}
@@ -610,26 +618,50 @@ export function Timeline({
610
618
  )}>
611
619
  <motion.div
612
620
  className={cn(
613
- "flex items-center justify-center rounded-full border-2 bg-background z-10",
614
- isMilestoneEvent ? "w-10 h-10" : "w-8 h-8",
621
+ "flex items-center justify-center rounded-full border-4 bg-background z-10 shadow-lg relative",
622
+ isMilestoneEvent ? "w-12 h-12" : "w-10 h-10",
615
623
  eventColor,
616
- isSelected && "ring-2 ring-ring ring-offset-2"
624
+ isSelected && "ring-4 ring-ring ring-offset-2"
617
625
  )}
618
- whileHover={{ scale: 1.1 }}
626
+ whileHover={{ scale: 1.15 }}
619
627
  whileTap={{ scale: 0.95 }}
620
628
  >
621
629
  {renderEventIcon(event)}
630
+
631
+ {/* Connector - Horizontal için node'un içinden çıkan çizgi */}
632
+ {!isLast && layout === 'horizontal' && (
633
+ <div
634
+ className={cn(
635
+ "absolute h-1",
636
+ gradientConnectors ? "bg-gradient-to-r from-border via-border/60 to-border/30" : "bg-border"
637
+ )}
638
+ style={{
639
+ width: '18.5rem', // Card genişliği (16rem) + margin (2rem) + node yarısı (0.5rem)
640
+ left: isMilestoneEvent ? '3rem' : '2.5rem', // Node genişliğinin yarısı
641
+ top: '50%',
642
+ transform: 'translateY(-50%)',
643
+ zIndex: -1
644
+ }}
645
+ />
646
+ )}
622
647
  </motion.div>
623
648
 
624
- {/* Connector */}
625
- {!isLast && renderConnector(event, events[index + 1], isLast)}
649
+ {/* Connector - Vertical layouts için */}
650
+ {!isLast && layout !== 'horizontal' && renderConnector(event, events[index + 1], isLast)}
626
651
  </div>
627
652
 
628
653
  {/* Event Content */}
629
- <div className={cn(
630
- "flex-1 min-w-0",
631
- layout === 'alternating' && index % 2 === 0 && "text-right"
632
- )}>
654
+ <Card
655
+ className={cn(
656
+ layout === 'horizontal' ? "w-64" : "flex-1 min-w-0",
657
+ "shadow-md hover:shadow-lg transition-shadow duration-200 cursor-pointer",
658
+ layout === 'alternating' && index % 2 === 0 && "text-right",
659
+ isSelected && "ring-2 ring-primary",
660
+ "bg-card/50 backdrop-blur-sm"
661
+ )}
662
+ onClick={() => handleEventClick(event)}
663
+ >
664
+ <CardContent className="p-4">
633
665
  {/* Header */}
634
666
  <div className="flex items-start justify-between gap-2 mb-2">
635
667
  <div className="flex-1">
@@ -880,7 +912,8 @@ export function Timeline({
880
912
  )}
881
913
  </Button>
882
914
  )}
883
- </div>
915
+ </CardContent>
916
+ </Card>
884
917
  </div>
885
918
  )
886
919
 
@@ -1011,9 +1044,13 @@ export function Timeline({
1011
1044
  ) : (
1012
1045
  <div className={cn(
1013
1046
  "space-y-0",
1014
- layout === 'horizontal' && "flex overflow-x-auto gap-6 pb-4",
1047
+ layout === 'horizontal' && "flex flex-row overflow-x-auto gap-0 pb-4 items-start",
1015
1048
  layout === 'alternating' && "relative"
1016
1049
  )}>
1050
+ {/* Center line for alternating layout */}
1051
+ {layout === 'alternating' && (
1052
+ <div className="absolute left-1/2 -ml-0.5 top-0 bottom-0 w-1 bg-border" />
1053
+ )}
1017
1054
  {filteredAndSortedEvents.map((event, index) =>
1018
1055
  renderEvent(event, index, index === filteredAndSortedEvents.length - 1)
1019
1056
  )}
@@ -1047,16 +1084,13 @@ export function Timeline({
1047
1084
  aria-label={ariaLabel}
1048
1085
  {...props}
1049
1086
  >
1087
+ {(showSearch || showFilter || showExport) && (
1050
1088
  <CardHeader className={cn(
1051
1089
  compactMode && "pb-3",
1052
1090
  printMode && "print:pb-2"
1053
1091
  )}>
1054
1092
  <div className="flex items-center justify-between">
1055
1093
  <div>
1056
- <CardTitle className="flex items-center gap-2">
1057
- <Clock className="h-5 w-5" />
1058
- Timeline
1059
- </CardTitle>
1060
1094
  <CardDescription>
1061
1095
  {filteredAndSortedEvents.length} event{filteredAndSortedEvents.length !== 1 ? 's' : ''}
1062
1096
  {searchQuery && ` matching "${searchQuery}"`}
@@ -1123,6 +1157,7 @@ export function Timeline({
1123
1157
  </div>
1124
1158
  )}
1125
1159
  </CardHeader>
1160
+ )}
1126
1161
 
1127
1162
  <CardContent className={cn(
1128
1163
  compactMode ? "pt-2" : "pt-6",