@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,1246 +0,0 @@
1
- "use client"
2
-
3
- import React from 'react'
4
- import {
5
- ResponsiveContainer,
6
- LineChart,
7
- BarChart,
8
- AreaChart,
9
- PieChart,
10
- ScatterChart,
11
- Line,
12
- Bar,
13
- Area,
14
- Pie,
15
- Cell,
16
- Scatter,
17
- XAxis,
18
- YAxis,
19
- CartesianGrid,
20
- Tooltip,
21
- Legend,
22
- ReferenceLine,
23
- ReferenceArea,
24
- Brush,
25
- ComposedChart,
26
- RadarChart,
27
- PolarGrid,
28
- PolarAngleAxis,
29
- PolarRadiusAxis,
30
- Radar,
31
- Treemap,
32
- Funnel,
33
- FunnelChart,
34
- Sankey,
35
- RadialBarChart,
36
- RadialBar,
37
- BarChart as RechartsBarChart,
38
- } from 'recharts'
39
- import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'
40
- import { Button } from '../ui/button'
41
- import {
42
- Download,
43
- Maximize2,
44
- Settings,
45
- RefreshCw,
46
- TrendingUp,
47
- TrendingDown,
48
- Minus,
49
- Lock,
50
- Sparkles,
51
- Eye,
52
- EyeOff,
53
- FileJson,
54
- FileSpreadsheet,
55
- Image,
56
- ZoomIn,
57
- ZoomOut,
58
- Move,
59
- Crosshair,
60
- Palette,
61
- Activity,
62
- MoreVertical,
63
- X,
64
- Check
65
- } from 'lucide-react'
66
- import { cn } from '../../lib/utils'
67
- import { motion, AnimatePresence, useMotionValue, useTransform } from 'framer-motion'
68
- import {
69
- DropdownMenu,
70
- DropdownMenuContent,
71
- DropdownMenuItem,
72
- DropdownMenuTrigger,
73
- DropdownMenuSeparator,
74
- } from '../ui/dropdown-menu'
75
- import { Skeleton } from '../ui/skeleton'
76
- import { Switch } from '../ui/switch'
77
- import { Label } from '../ui/label'
78
- import { Slider } from '../ui/slider'
79
- import {
80
- Popover,
81
- PopoverContent,
82
- PopoverTrigger,
83
- } from '../ui/popover'
84
-
85
- export type ChartType = 'line' | 'bar' | 'area' | 'pie' | 'scatter' | 'composed' | 'radar' | 'radialBar' | 'treemap' | 'funnel'
86
-
87
- export interface ChartDataPoint {
88
- [key: string]: string | number | null
89
- }
90
-
91
- export interface ChartSeries {
92
- dataKey: string
93
- name: string
94
- color: string
95
- type?: 'monotone' | 'linear' | 'step' | 'basis' | 'basisClosed' | 'basisOpen' | 'natural'
96
- strokeWidth?: number
97
- fillOpacity?: number
98
- hide?: boolean
99
- gradient?: boolean
100
- strokeDasharray?: string
101
- dot?: boolean | object
102
- activeDot?: boolean | object
103
- label?: boolean | object
104
- stackId?: string
105
- yAxisId?: string
106
- }
107
-
108
- interface AdvancedChartProps {
109
- data: ChartDataPoint[]
110
- type: ChartType
111
- series: ChartSeries[]
112
- title?: string
113
- subtitle?: string
114
- height?: number
115
- width?: number | string
116
- xAxisKey?: string
117
- yAxisKey?: string
118
- showGrid?: boolean
119
- showTooltip?: boolean
120
- showLegend?: boolean
121
- showBrush?: boolean
122
- showReference?: boolean
123
- referenceLines?: Array<{
124
- value: number
125
- label?: string
126
- color?: string
127
- }>
128
- referenceAreas?: Array<{
129
- x1: string | number
130
- x2: string | number
131
- label?: string
132
- color?: string
133
- }>
134
- colors?: string[]
135
- className?: string
136
- onDataPointClick?: (data: ChartDataPoint) => void
137
- onExport?: (format: 'png' | 'svg' | 'pdf' | 'json' | 'csv') => void
138
- onRefresh?: () => void
139
- customTooltip?: React.ComponentType<any>
140
- customLegend?: React.ComponentType<any>
141
- loading?: boolean
142
- error?: string | null
143
- animated?: boolean
144
- responsive?: boolean
145
- showCrosshair?: boolean
146
- enableZoom?: boolean
147
- enablePan?: boolean
148
- showMiniMap?: boolean
149
- darkMode?: boolean
150
- gradientColors?: boolean
151
- interactiveLegend?: boolean
152
- showDataLabels?: boolean
153
- sparklineMode?: boolean
154
- animationDuration?: number
155
- theme?: 'default' | 'vibrant' | 'pastel' | 'dark' | 'neon'
156
- }
157
-
158
- // Tema renk paletleri
159
- const COLOR_THEMES = {
160
- default: [
161
- '#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6',
162
- '#06b6d4', '#f97316', '#84cc16', '#ec4899', '#6366f1'
163
- ],
164
- vibrant: [
165
- '#FF006E', '#FB5607', '#FFBE0B', '#8338EC', '#3A86FF',
166
- '#06FFB4', '#FF4365', '#00F5FF', '#FF124F', '#7209B7'
167
- ],
168
- pastel: [
169
- '#B5E2FA', '#F7AEF8', '#FDC5F5', '#F8B7D3', '#FAC9B8',
170
- '#C8E9A0', '#A7D2CB', '#F2D98D', '#E5B3BB', '#D6A2E8'
171
- ],
172
- dark: [
173
- '#1e3a5f', '#4b0e0e', '#0e4b2b', '#4b3c0e', '#2e0e4b',
174
- '#0e3c4b', '#4b2e0e', '#2b4b0e', '#4b0e3c', '#1e0e4b'
175
- ],
176
- neon: [
177
- '#39FF14', '#FF10F0', '#00FFFF', '#FE019A', '#FFFF00',
178
- '#FF073A', '#00FF00', '#FF0099', '#0FF0FC', '#FFE400'
179
- ]
180
- }
181
-
182
- const DEFAULT_COLORS = COLOR_THEMES.default
183
-
184
- // Özel Tooltip bileşeni
185
- const CustomTooltip: React.FC<any> = ({ active, payload, label }) => {
186
- if (!active || !payload || !payload.length) return null
187
-
188
- return (
189
- <motion.div
190
- initial={{ opacity: 0, scale: 0.9 }}
191
- animate={{ opacity: 1, scale: 1 }}
192
- exit={{ opacity: 0, scale: 0.9 }}
193
- className="backdrop-blur-xl bg-background/80 p-3 rounded-xl shadow-2xl border border-border/50"
194
- >
195
- <p className="text-sm font-medium mb-2">{label}</p>
196
- {payload.map((entry: any, index: number) => (
197
- <div key={index} className="flex items-center gap-2 text-sm">
198
- <div
199
- className="w-3 h-3 rounded-full"
200
- style={{ backgroundColor: entry.color }}
201
- />
202
- <span className="text-muted-foreground">{entry.name}:</span>
203
- <span className="font-semibold">{entry.value}</span>
204
- </div>
205
- ))}
206
- </motion.div>
207
- )
208
- }
209
-
210
- // Özel Legend bileşeni
211
- const CustomLegend: React.FC<any> = ({ payload, onItemClick }) => {
212
- return (
213
- <div className="flex flex-wrap items-center justify-center gap-4 mt-4">
214
- {payload.map((entry: any, index: number) => (
215
- <motion.button
216
- key={`item-${index}`}
217
- whileHover={{ scale: 1.05 }}
218
- whileTap={{ scale: 0.95 }}
219
- onClick={() => onItemClick && onItemClick(entry)}
220
- className={cn(
221
- "flex items-center gap-2 px-3 py-1.5 rounded-lg transition-all",
222
- "hover:bg-accent/10",
223
- entry.inactive && "opacity-50"
224
- )}
225
- >
226
- <motion.div
227
- animate={{
228
- scale: entry.inactive ? 0.8 : 1,
229
- opacity: entry.inactive ? 0.5 : 1
230
- }}
231
- className="w-3 h-3 rounded-full"
232
- style={{ backgroundColor: entry.color }}
233
- />
234
- <span className="text-sm font-medium">{entry.value}</span>
235
- </motion.button>
236
- ))}
237
- </div>
238
- )
239
- }
240
-
241
- // Mini sparkline bileşeni
242
- const SparklinePreview = ({ data, dataKey, color }: any) => {
243
- const values = data.map((d: any) => d[dataKey])
244
- const min = Math.min(...values)
245
- const max = Math.max(...values)
246
- const range = max - min
247
-
248
- return (
249
- <svg width="60" height="20" className="ml-2">
250
- <polyline
251
- fill="none"
252
- stroke={color}
253
- strokeWidth="2"
254
- points={values.map((v: number, i: number) =>
255
- `${(i / (values.length - 1)) * 60},${20 - ((v - min) / range) * 20}`
256
- ).join(' ')}
257
- />
258
- </svg>
259
- )
260
- }
261
-
262
- // Loading Skeleton bileşeni
263
- const ChartSkeleton = ({ height }: { height: number }) => (
264
- <div className="w-full" style={{ height }}>
265
- <div className="flex items-end justify-center h-full gap-2 px-8">
266
- {[...Array(8)].map((_, i) => (
267
- <motion.div
268
- key={i}
269
- initial={{ height: 0 }}
270
- animate={{ height: `${Math.random() * 80 + 20}%` }}
271
- transition={{
272
- duration: 1.5,
273
- delay: i * 0.1,
274
- repeat: Infinity,
275
- repeatType: "reverse"
276
- }}
277
- className="w-full bg-gradient-to-t from-primary/20 to-primary/5 rounded-t-lg"
278
- />
279
- ))}
280
- </div>
281
- </div>
282
- )
283
-
284
- export function AdvancedChart({
285
- data,
286
- type,
287
- series,
288
- title,
289
- subtitle,
290
- height = 400,
291
- width = '100%',
292
- xAxisKey = 'name',
293
- yAxisKey,
294
- showGrid = true,
295
- showTooltip = true,
296
- showLegend = true,
297
- showBrush = false,
298
- showReference = false,
299
- referenceLines = [],
300
- referenceAreas = [],
301
- colors = DEFAULT_COLORS,
302
- className,
303
- onDataPointClick,
304
- onExport,
305
- onRefresh,
306
- customTooltip,
307
- customLegend,
308
- loading = false,
309
- error = null,
310
- animated = true,
311
- responsive = true,
312
- showCrosshair = false,
313
- enableZoom = false,
314
- enablePan = false,
315
- showMiniMap = false,
316
- darkMode = false,
317
- gradientColors = true,
318
- interactiveLegend = true,
319
- showDataLabels = false,
320
- sparklineMode = false,
321
- animationDuration = 1500,
322
- theme = 'default',
323
- }: AdvancedChartProps) {
324
- const [isFullscreen, setIsFullscreen] = React.useState(false)
325
- const [showSettings, setShowSettings] = React.useState(false)
326
- const [zoomLevel, setZoomLevel] = React.useState(100)
327
- const [selectedTheme, setSelectedTheme] = React.useState(theme)
328
- const [hoveredDataPoint, setHoveredDataPoint] = React.useState<any>(null)
329
- const [hiddenSeries, setHiddenSeries] = React.useState<Set<string>>(new Set())
330
- const chartRef = React.useRef<HTMLDivElement>(null)
331
- const containerRef = React.useRef<HTMLDivElement>(null)
332
-
333
- // Tema renklerini al
334
- const themeColors = React.useMemo(() => {
335
- return COLOR_THEMES[selectedTheme] || DEFAULT_COLORS
336
- }, [selectedTheme])
337
-
338
- // Gradient tanımlamaları için benzersiz ID oluştur
339
- const gradientId = React.useId()
340
-
341
- // Calculate trend for the first series
342
- const trend = React.useMemo(() => {
343
- if (!data || !Array.isArray(data) || !data.length || !series || !Array.isArray(series) || !series.length) return null
344
-
345
- const firstSeries = series[0]
346
- if (!firstSeries || !firstSeries.dataKey) return null
347
-
348
- const values = data.map(d => Number(d[firstSeries.dataKey])).filter(v => !isNaN(v))
349
-
350
- if (values.length < 2) return null
351
-
352
- const first = values[0]
353
- const last = values[values.length - 1]
354
-
355
- if (first === 0) return null // Avoid division by zero
356
-
357
- const change = ((last - first) / first) * 100
358
-
359
- return {
360
- direction: change > 0 ? 'up' : change < 0 ? 'down' : 'neutral',
361
- percentage: Math.abs(change).toFixed(1)
362
- }
363
- }, [data, series])
364
-
365
- // Seri görünürlüğünü toggle et
366
- const toggleSeriesVisibility = (dataKey: string) => {
367
- setHiddenSeries(prev => {
368
- const newSet = new Set(prev)
369
- if (newSet.has(dataKey)) {
370
- newSet.delete(dataKey)
371
- } else {
372
- newSet.add(dataKey)
373
- }
374
- return newSet
375
- })
376
- }
377
-
378
- // Legend item click handler
379
- const handleLegendItemClick = (entry: any) => {
380
- if (interactiveLegend) {
381
- toggleSeriesVisibility(entry.dataKey)
382
- }
383
- }
384
-
385
- const handleExport = (format: 'png' | 'svg' | 'pdf' | 'json' | 'csv') => {
386
- if (onExport) {
387
- onExport(format)
388
- }
389
- }
390
-
391
- // Zoom kontrolü
392
- const handleZoom = (direction: 'in' | 'out') => {
393
- setZoomLevel(prev => {
394
- if (direction === 'in') return Math.min(prev + 10, 200)
395
- return Math.max(prev - 10, 50)
396
- })
397
- }
398
-
399
- // Gradient tanımlamaları oluştur
400
- const renderGradientDefs = () => (
401
- <defs>
402
- {series.map((s, index) => {
403
- const color = s.color || themeColors[index % themeColors.length]
404
- return (
405
- <linearGradient
406
- key={`gradient-${s.dataKey}`}
407
- id={`gradient-${gradientId}-${s.dataKey}`}
408
- x1="0" y1="0" x2="0" y2="1"
409
- >
410
- <stop offset="0%" stopColor={color} stopOpacity={0.8} />
411
- <stop offset="100%" stopColor={color} stopOpacity={0.1} />
412
- </linearGradient>
413
- )
414
- })}
415
- </defs>
416
- )
417
-
418
- const renderChart = () => {
419
- // Güvenlik kontrolleri
420
- if (!data || !Array.isArray(data) || !series || !Array.isArray(series)) {
421
- return <div className="flex items-center justify-center h-full text-muted-foreground">No data available</div>
422
- }
423
-
424
- const commonProps = {
425
- data,
426
- width: typeof width === 'string' ? undefined : width,
427
- height: sparklineMode ? 60 : height,
428
- margin: sparklineMode
429
- ? { top: 5, right: 5, left: 5, bottom: 5 }
430
- : { top: 20, right: 30, left: 20, bottom: showBrush ? 60 : 20 },
431
- }
432
-
433
- const visibleSeries = series.filter(s => !s.hide && !hiddenSeries.has(s.dataKey))
434
-
435
- // Axis stil özellikleri
436
- const axisStyle = {
437
- fontSize: 12,
438
- fill: darkMode ? '#9ca3af' : '#6b7280',
439
- }
440
-
441
- // Grid stil özellikleri
442
- const gridStyle = {
443
- stroke: darkMode ? '#374151' : '#e5e7eb',
444
- strokeDasharray: '3 3',
445
- opacity: 0.5
446
- }
447
-
448
- switch (type) {
449
- case 'line':
450
- return (
451
- <LineChart {...commonProps}>
452
- {gradientColors && renderGradientDefs()}
453
- {showGrid && !sparklineMode && (
454
- <CartesianGrid {...gridStyle} />
455
- )}
456
- {!sparklineMode && (
457
- <>
458
- <XAxis
459
- dataKey={xAxisKey}
460
- {...axisStyle}
461
- tick={{ fontSize: 11 }}
462
- axisLine={{ stroke: darkMode ? '#4b5563' : '#d1d5db' }}
463
- />
464
- <YAxis
465
- {...axisStyle}
466
- tick={{ fontSize: 11 }}
467
- axisLine={{ stroke: darkMode ? '#4b5563' : '#d1d5db' }}
468
- />
469
- </>
470
- )}
471
- {showTooltip && !sparklineMode && (
472
- <Tooltip
473
- cursor={showCrosshair ? {
474
- stroke: darkMode ? '#6b7280' : '#9ca3af',
475
- strokeWidth: 1,
476
- strokeDasharray: '5 5'
477
- } : false}
478
- />
479
- )}
480
- {showLegend && !sparklineMode && (
481
- <Legend
482
- content={(props) => {
483
- const CustomLegendComponent = customLegend
484
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
485
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
486
- }}
487
- />
488
- )}
489
- {visibleSeries.map((s, index) => {
490
- const color = s.color || themeColors[index % themeColors.length]
491
- return (
492
- <Line
493
- key={s.dataKey}
494
- type={s.type || 'monotone'}
495
- dataKey={s.dataKey}
496
- stroke={color}
497
- strokeWidth={sparklineMode ? 1.5 : (s.strokeWidth || 2)}
498
- strokeDasharray={s.strokeDasharray}
499
- name={s.name}
500
- dot={sparklineMode ? false : (s.dot !== undefined ? s.dot : {
501
- r: 3,
502
- strokeWidth: 2,
503
- fill: darkMode ? '#1f2937' : '#ffffff'
504
- })}
505
- activeDot={sparklineMode ? false : (s.activeDot !== undefined ? s.activeDot : {
506
- r: 5,
507
- strokeWidth: 2,
508
- fill: color,
509
- stroke: darkMode ? '#1f2937' : '#ffffff',
510
- className: 'animate-pulse'
511
- })}
512
- animationDuration={animated ? animationDuration : 0}
513
- animationBegin={index * 100}
514
- label={showDataLabels ? s.label : false}
515
- />
516
- )
517
- })}
518
- {showReference && referenceLines.map((ref, index) => (
519
- <ReferenceLine
520
- key={index}
521
- y={ref.value}
522
- stroke={ref.color || (darkMode ? '#6b7280' : '#9ca3af')}
523
- strokeDasharray="5 5"
524
- label={ref.label}
525
- />
526
- ))}
527
- {showReference && referenceAreas.map((ref, index) => (
528
- <ReferenceArea
529
- key={index}
530
- x1={ref.x1}
531
- x2={ref.x2}
532
- fill={ref.color || (darkMode ? '#374151' : '#f3f4f6')}
533
- fillOpacity={0.3}
534
- label={ref.label}
535
- />
536
- ))}
537
- {showBrush && !sparklineMode && (
538
- <Brush
539
- height={30}
540
- fill={darkMode ? '#1f2937' : '#f9fafb'}
541
- stroke={darkMode ? '#374151' : '#e5e7eb'}
542
- />
543
- )}
544
- </LineChart>
545
- )
546
-
547
- case 'bar':
548
- return (
549
- <BarChart {...commonProps}>
550
- {gradientColors && renderGradientDefs()}
551
- {showGrid && <CartesianGrid {...gridStyle} />}
552
- <XAxis
553
- dataKey={xAxisKey}
554
- {...axisStyle}
555
- tick={{ fontSize: 11 }}
556
- axisLine={{ stroke: darkMode ? '#4b5563' : '#d1d5db' }}
557
- />
558
- <YAxis
559
- {...axisStyle}
560
- tick={{ fontSize: 11 }}
561
- axisLine={{ stroke: darkMode ? '#4b5563' : '#d1d5db' }}
562
- />
563
- {showTooltip && (
564
- <Tooltip
565
- cursor={{ fill: darkMode ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)' }}
566
- />
567
- )}
568
- {showLegend && (
569
- <Legend
570
- content={(props) => {
571
- const CustomLegendComponent = customLegend
572
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
573
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
574
- }}
575
- />
576
- )}
577
- {visibleSeries.map((s, index) => {
578
- const color = s.color || themeColors[index % themeColors.length]
579
- return (
580
- <Bar
581
- key={s.dataKey}
582
- dataKey={s.dataKey}
583
- fill={gradientColors ? `url(#gradient-${gradientId}-${s.dataKey})` : color}
584
- name={s.name}
585
- animationDuration={animated ? animationDuration : 0}
586
- animationBegin={index * 100}
587
- radius={[4, 4, 0, 0]}
588
- label={showDataLabels ? s.label : false}
589
- stackId={s.stackId}
590
- onMouseEnter={() => setHoveredDataPoint(s.dataKey)}
591
- onMouseLeave={() => setHoveredDataPoint(null)}
592
- className="hover:opacity-80 transition-opacity cursor-pointer"
593
- />
594
- )
595
- })}
596
- {showReference && referenceLines.map((ref, index) => (
597
- <ReferenceLine
598
- key={index}
599
- y={ref.value}
600
- stroke={ref.color || (darkMode ? '#6b7280' : '#9ca3af')}
601
- strokeDasharray="5 5"
602
- label={ref.label}
603
- />
604
- ))}
605
- {showBrush && (
606
- <Brush
607
- height={30}
608
- fill={darkMode ? '#1f2937' : '#f9fafb'}
609
- stroke={darkMode ? '#374151' : '#e5e7eb'}
610
- />
611
- )}
612
- </BarChart>
613
- )
614
-
615
- case 'area':
616
- return (
617
- <AreaChart {...commonProps}>
618
- {gradientColors && renderGradientDefs()}
619
- {showGrid && <CartesianGrid {...gridStyle} />}
620
- <XAxis
621
- dataKey={xAxisKey}
622
- {...axisStyle}
623
- tick={{ fontSize: 11 }}
624
- axisLine={{ stroke: darkMode ? '#4b5563' : '#d1d5db' }}
625
- />
626
- <YAxis
627
- {...axisStyle}
628
- tick={{ fontSize: 11 }}
629
- axisLine={{ stroke: darkMode ? '#4b5563' : '#d1d5db' }}
630
- />
631
- {showTooltip && <Tooltip />}
632
- {showLegend && (
633
- <Legend
634
- content={(props) => {
635
- const CustomLegendComponent = customLegend
636
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
637
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
638
- }}
639
- />
640
- )}
641
- {visibleSeries.map((s, index) => {
642
- const color = s.color || themeColors[index % themeColors.length]
643
- return (
644
- <Area
645
- key={s.dataKey}
646
- type={s.type || 'monotone'}
647
- dataKey={s.dataKey}
648
- stroke={color}
649
- strokeWidth={2}
650
- fill={gradientColors ? `url(#gradient-${gradientId}-${s.dataKey})` : color}
651
- fillOpacity={s.fillOpacity || 0.6}
652
- name={s.name}
653
- animationDuration={animated ? animationDuration : 0}
654
- animationBegin={index * 100}
655
- dot={s.dot !== undefined ? s.dot : false}
656
- activeDot={s.activeDot !== undefined ? s.activeDot : {
657
- r: 4,
658
- strokeWidth: 2,
659
- fill: color,
660
- stroke: darkMode ? '#1f2937' : '#ffffff',
661
- className: 'animate-pulse'
662
- }}
663
- label={showDataLabels ? s.label : false}
664
- stackId={s.stackId}
665
- />
666
- )
667
- })}
668
- {showReference && referenceLines.map((ref, index) => (
669
- <ReferenceLine
670
- key={index}
671
- y={ref.value}
672
- stroke={ref.color || (darkMode ? '#6b7280' : '#9ca3af')}
673
- strokeDasharray="5 5"
674
- label={ref.label}
675
- />
676
- ))}
677
- {showBrush && (
678
- <Brush
679
- height={30}
680
- fill={darkMode ? '#1f2937' : '#f9fafb'}
681
- stroke={darkMode ? '#374151' : '#e5e7eb'}
682
- />
683
- )}
684
- </AreaChart>
685
- )
686
-
687
- case 'pie':
688
- return (
689
- <PieChart {...commonProps}>
690
- {showTooltip && (
691
- <Tooltip
692
- /* @ts-ignore */
693
- content={customTooltip ? customTooltip : CustomTooltip}
694
- />
695
- )}
696
- {showLegend && (
697
- <Legend
698
- content={(props) => {
699
- const CustomLegendComponent = customLegend
700
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
701
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
702
- }}
703
- />
704
- )}
705
- <Pie
706
- data={data}
707
- dataKey={series[0]?.dataKey || 'value'}
708
- nameKey={xAxisKey}
709
- cx="50%"
710
- cy="50%"
711
- innerRadius={type === 'pie' ? 0 : '40%'}
712
- outerRadius={Math.min(height, typeof width === 'number' ? width : 400) / 3}
713
- animationDuration={animated ? animationDuration : 0}
714
- animationBegin={0}
715
- label={showDataLabels ? {
716
- fill: darkMode ? '#e5e7eb' : '#374151',
717
- fontSize: 12
718
- } : false}
719
- labelLine={showDataLabels ? {
720
- stroke: darkMode ? '#6b7280' : '#9ca3af',
721
- strokeWidth: 1
722
- } : false}
723
- >
724
- {data.map((entry, index) => {
725
- const color = themeColors[index % themeColors.length]
726
- return (
727
- <Cell
728
- key={`cell-${index}`}
729
- fill={color}
730
- className="hover:opacity-80 transition-opacity cursor-pointer"
731
- onMouseEnter={() => setHoveredDataPoint(entry)}
732
- onMouseLeave={() => setHoveredDataPoint(null)}
733
- />
734
- )
735
- })}
736
- </Pie>
737
- </PieChart>
738
- )
739
-
740
- case 'scatter':
741
- return (
742
- <ScatterChart {...commonProps}>
743
- {showGrid && <CartesianGrid {...gridStyle} />}
744
- <XAxis
745
- dataKey={xAxisKey}
746
- {...axisStyle}
747
- tick={{ fontSize: 11 }}
748
- axisLine={{ stroke: darkMode ? '#4b5563' : '#d1d5db' }}
749
- />
750
- <YAxis
751
- dataKey={yAxisKey}
752
- {...axisStyle}
753
- tick={{ fontSize: 11 }}
754
- axisLine={{ stroke: darkMode ? '#4b5563' : '#d1d5db' }}
755
- />
756
- {showTooltip && <Tooltip />}
757
- {showLegend && (
758
- <Legend
759
- content={(props) => {
760
- const CustomLegendComponent = customLegend
761
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
762
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
763
- }}
764
- />
765
- )}
766
- {visibleSeries.map((s, index) => {
767
- const color = s.color || themeColors[index % themeColors.length]
768
- return (
769
- <Scatter
770
- key={s.dataKey}
771
- data={data}
772
- fill={color}
773
- name={s.name}
774
- animationDuration={animated ? animationDuration : 0}
775
- animationBegin={index * 100}
776
- shape={(props: any) => {
777
- const isHovered = hoveredDataPoint === props.payload
778
- // Extract only the necessary props for the circle element
779
- const { cx, cy, fill } = props
780
- return (
781
- <circle
782
- cx={cx}
783
- cy={cy}
784
- fill={fill}
785
- r={isHovered ? 6 : 4}
786
- className="transition-all duration-200 hover:r-6"
787
- onMouseEnter={() => setHoveredDataPoint(props.payload)}
788
- onMouseLeave={() => setHoveredDataPoint(null)}
789
- />
790
- )
791
- }}
792
- />
793
- )
794
- })}
795
- </ScatterChart>
796
- )
797
-
798
- case 'radar':
799
- return (
800
- <RadarChart {...commonProps}>
801
- <PolarGrid
802
- stroke={darkMode ? '#374151' : '#e5e7eb'}
803
- strokeDasharray="3 3"
804
- />
805
- <PolarAngleAxis
806
- dataKey={xAxisKey}
807
- {...axisStyle}
808
- tick={{ fontSize: 10 }}
809
- />
810
- <PolarRadiusAxis
811
- {...axisStyle}
812
- tick={{ fontSize: 10 }}
813
- axisLine={false}
814
- />
815
- {showTooltip && (
816
- <Tooltip
817
- /* @ts-ignore */
818
- content={customTooltip ? customTooltip : CustomTooltip}
819
- />
820
- )}
821
- {showLegend && (
822
- <Legend
823
- content={(props) => {
824
- const CustomLegendComponent = customLegend
825
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
826
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
827
- }}
828
- />
829
- )}
830
- {visibleSeries.map((s, index) => {
831
- const color = s.color || themeColors[index % themeColors.length]
832
- return (
833
- <Radar
834
- key={s.dataKey}
835
- dataKey={s.dataKey}
836
- stroke={color}
837
- fill={color}
838
- fillOpacity={0.3}
839
- name={s.name}
840
- animationDuration={animated ? animationDuration : 0}
841
- animationBegin={index * 100}
842
- />
843
- )
844
- })}
845
- </RadarChart>
846
- )
847
-
848
- case 'radialBar':
849
- return (
850
- <RadialBarChart
851
- {...commonProps}
852
- innerRadius="10%"
853
- outerRadius="90%"
854
- >
855
- {showTooltip && (
856
- <Tooltip
857
- /* @ts-ignore */
858
- content={customTooltip ? customTooltip : CustomTooltip}
859
- />
860
- )}
861
- {showLegend && (
862
- <Legend
863
- content={(props) => {
864
- const CustomLegendComponent = customLegend
865
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
866
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
867
- }}
868
- />
869
- )}
870
- <RadialBar
871
- dataKey={series[0]?.dataKey || 'value'}
872
- cornerRadius={10}
873
- fill={themeColors[0]}
874
- animationDuration={animated ? animationDuration : 0}
875
- label={showDataLabels ? {
876
- fill: darkMode ? '#e5e7eb' : '#374151',
877
- position: 'center'
878
- } : false}
879
- />
880
- </RadialBarChart>
881
- )
882
-
883
- default:
884
- return <div>Unsupported chart type</div>
885
- }
886
- }
887
-
888
- if (loading) {
889
- return (
890
- <Card className={cn("w-full overflow-hidden", className)}>
891
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
892
- <div className="space-y-1">
893
- <Skeleton className="h-4 w-32" />
894
- <Skeleton className="h-3 w-48" />
895
- </div>
896
- <div className="flex items-center space-x-1">
897
- <Skeleton className="h-8 w-8 rounded" />
898
- <Skeleton className="h-8 w-8 rounded" />
899
- <Skeleton className="h-8 w-8 rounded" />
900
- </div>
901
- </CardHeader>
902
- <CardContent>
903
- <ChartSkeleton height={height} />
904
- </CardContent>
905
- </Card>
906
- )
907
- }
908
-
909
- if (error) {
910
- return (
911
- <Card className={cn("w-full", className)}>
912
- <CardContent className="flex items-center justify-center" style={{ height }}>
913
- <motion.div
914
- initial={{ opacity: 0, scale: 0.9 }}
915
- animate={{ opacity: 1, scale: 1 }}
916
- className="text-center"
917
- >
918
- <div className="w-12 h-12 rounded-full bg-destructive/10 dark:bg-destructive/20 flex items-center justify-center mx-auto mb-4">
919
- <X className="w-6 h-6 text-destructive" />
920
- </div>
921
- <p className="text-destructive mb-4 font-medium">{error}</p>
922
- {onRefresh && (
923
- <Button
924
- variant="outline"
925
- onClick={onRefresh}
926
- className="group"
927
- >
928
- <RefreshCw className="mr-2 h-4 w-4 group-hover:rotate-180 transition-transform duration-500" />
929
- Retry
930
- </Button>
931
- )}
932
- </motion.div>
933
- </CardContent>
934
- </Card>
935
- )
936
- }
937
-
938
- return (
939
- <motion.div
940
- ref={containerRef}
941
- className={cn(
942
- "relative",
943
- isFullscreen && "fixed inset-0 z-50 bg-background p-4"
944
- )}
945
- initial={{ opacity: 0, y: 20 }}
946
- animate={{ opacity: 1, y: 0 }}
947
- transition={{ duration: 0.5 }}
948
- >
949
- <Card className={cn(
950
- "w-full overflow-hidden transition-all duration-300",
951
- darkMode && "dark",
952
- isFullscreen && "h-full",
953
- className
954
- )} ref={chartRef}>
955
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
956
- <motion.div
957
- className="space-y-1"
958
- initial={{ opacity: 0, x: -20 }}
959
- animate={{ opacity: 1, x: 0 }}
960
- transition={{ delay: 0.1 }}
961
- >
962
- <CardTitle className="text-base font-medium flex items-center gap-2">
963
- {title}
964
- {trend && (
965
- <motion.span
966
- className="inline-flex items-center"
967
- initial={{ scale: 0 }}
968
- animate={{ scale: 1 }}
969
- transition={{ type: "spring", delay: 0.3 }}
970
- >
971
- {trend.direction === 'up' && (
972
- <TrendingUp className="h-4 w-4 text-green-500 dark:text-green-400" />
973
- )}
974
- {trend.direction === 'down' && (
975
- <TrendingDown className="h-4 w-4 text-red-500 dark:text-red-400" />
976
- )}
977
- {trend.direction === 'neutral' && (
978
- <Minus className="h-4 w-4 text-muted-foreground" />
979
- )}
980
- <span className={cn(
981
- "ml-1 text-sm font-semibold",
982
- trend.direction === 'up' && "text-green-500 dark:text-green-400",
983
- trend.direction === 'down' && "text-red-500 dark:text-red-400",
984
- trend.direction === 'neutral' && "text-muted-foreground"
985
- )}>
986
- {trend.percentage}%
987
- </span>
988
- </motion.span>
989
- )}
990
- {sparklineMode && series[0] && (
991
- <SparklinePreview
992
- data={data}
993
- dataKey={series[0].dataKey}
994
- color={series[0].color || themeColors[0]}
995
- />
996
- )}
997
- </CardTitle>
998
- {subtitle && (
999
- <p className="text-xs text-muted-foreground">{subtitle}</p>
1000
- )}
1001
- </motion.div>
1002
-
1003
- <motion.div
1004
- className="flex items-center space-x-1"
1005
- initial={{ opacity: 0, x: 20 }}
1006
- animate={{ opacity: 1, x: 0 }}
1007
- transition={{ delay: 0.2 }}
1008
- >
1009
- {/* Zoom controls */}
1010
- {enableZoom && !sparklineMode && (
1011
- <div className="flex items-center border rounded-lg mr-2">
1012
- <Button
1013
- variant="ghost"
1014
- size="sm"
1015
- onClick={() => handleZoom('out')}
1016
- disabled={zoomLevel <= 50}
1017
- className="h-7 w-7 p-0"
1018
- >
1019
- <ZoomOut className="h-3 w-3" />
1020
- </Button>
1021
- <span className="text-xs px-2 text-muted-foreground">
1022
- {zoomLevel}%
1023
- </span>
1024
- <Button
1025
- variant="ghost"
1026
- size="sm"
1027
- onClick={() => handleZoom('in')}
1028
- disabled={zoomLevel >= 200}
1029
- className="h-7 w-7 p-0"
1030
- >
1031
- <ZoomIn className="h-3 w-3" />
1032
- </Button>
1033
- </div>
1034
- )}
1035
-
1036
- {/* Other controls */}
1037
- {onRefresh && (
1038
- <Button
1039
- variant="ghost"
1040
- size="sm"
1041
- onClick={onRefresh}
1042
- className="group"
1043
- >
1044
- <RefreshCw className="h-4 w-4 group-hover:rotate-180 transition-transform duration-500" />
1045
- </Button>
1046
- )}
1047
-
1048
- {/* Settings popover */}
1049
- <Popover open={showSettings} onOpenChange={setShowSettings}>
1050
- <PopoverTrigger asChild>
1051
- <Button variant="ghost" size="sm">
1052
- <Settings className="h-4 w-4" />
1053
- </Button>
1054
- </PopoverTrigger>
1055
- <PopoverContent className="w-80" align="end">
1056
- <div className="space-y-4">
1057
- <div className="space-y-2">
1058
- <h4 className="font-medium text-sm">Chart Settings</h4>
1059
- </div>
1060
-
1061
- {/* Theme selector */}
1062
- <div className="space-y-2">
1063
- <Label className="text-xs">Color Theme</Label>
1064
- <div className="grid grid-cols-3 gap-2">
1065
- {Object.entries(COLOR_THEMES).map(([themeName, colors]) => (
1066
- <button
1067
- key={themeName}
1068
- onClick={() => setSelectedTheme(themeName as any)}
1069
- className={cn(
1070
- "p-2 rounded-lg border-2 transition-all",
1071
- selectedTheme === themeName
1072
- ? "border-primary"
1073
- : "border-transparent hover:border-muted-foreground/20"
1074
- )}
1075
- >
1076
- <div className="flex gap-1">
1077
- {colors.slice(0, 3).map((color, i) => (
1078
- <div
1079
- key={i}
1080
- className="w-3 h-3 rounded-full"
1081
- style={{ backgroundColor: color }}
1082
- />
1083
- ))}
1084
- </div>
1085
- <span className="text-xs mt-1 block capitalize">
1086
- {themeName}
1087
- </span>
1088
- </button>
1089
- ))}
1090
- </div>
1091
- </div>
1092
-
1093
- {/* Animation toggle */}
1094
- <div className="flex items-center justify-between">
1095
- <Label htmlFor="animation" className="text-xs">
1096
- Animations
1097
- </Label>
1098
- <Switch
1099
- id="animation"
1100
- checked={animated}
1101
- onCheckedChange={(checked) => {
1102
- // Update animated prop
1103
- }}
1104
- />
1105
- </div>
1106
-
1107
- {/* Grid toggle */}
1108
- <div className="flex items-center justify-between">
1109
- <Label htmlFor="grid" className="text-xs">
1110
- Show Grid
1111
- </Label>
1112
- <Switch
1113
- id="grid"
1114
- checked={showGrid}
1115
- onCheckedChange={(checked) => {
1116
- // Update showGrid prop
1117
- }}
1118
- />
1119
- </div>
1120
-
1121
- {/* Crosshair toggle */}
1122
- <div className="flex items-center justify-between">
1123
- <Label htmlFor="crosshair" className="text-xs">
1124
- Crosshair Cursor
1125
- </Label>
1126
- <Switch
1127
- id="crosshair"
1128
- checked={showCrosshair}
1129
- onCheckedChange={(checked) => {
1130
- // Update showCrosshair prop
1131
- }}
1132
- />
1133
- </div>
1134
- </div>
1135
- </PopoverContent>
1136
- </Popover>
1137
-
1138
- {/* Export menu */}
1139
- {onExport && (
1140
- <DropdownMenu>
1141
- <DropdownMenuTrigger asChild>
1142
- <Button variant="ghost" size="sm">
1143
- <Download className="h-4 w-4" />
1144
- </Button>
1145
- </DropdownMenuTrigger>
1146
- <DropdownMenuContent align="end">
1147
- <DropdownMenuItem onClick={() => handleExport('png')}>
1148
- <Image className="mr-2 h-4 w-4" />
1149
- Export as PNG
1150
- </DropdownMenuItem>
1151
- <DropdownMenuItem onClick={() => handleExport('svg')}>
1152
- <FileSpreadsheet className="mr-2 h-4 w-4" />
1153
- Export as SVG
1154
- </DropdownMenuItem>
1155
- <DropdownMenuItem onClick={() => handleExport('pdf')}>
1156
- <FileSpreadsheet className="mr-2 h-4 w-4" />
1157
- Export as PDF
1158
- </DropdownMenuItem>
1159
- <DropdownMenuSeparator />
1160
- <DropdownMenuItem onClick={() => handleExport('csv')}>
1161
- <FileSpreadsheet className="mr-2 h-4 w-4" />
1162
- Export as CSV
1163
- </DropdownMenuItem>
1164
- <DropdownMenuItem onClick={() => handleExport('json')}>
1165
- <FileJson className="mr-2 h-4 w-4" />
1166
- Export as JSON
1167
- </DropdownMenuItem>
1168
- </DropdownMenuContent>
1169
- </DropdownMenu>
1170
- )}
1171
-
1172
- {/* Fullscreen toggle */}
1173
- <Button
1174
- variant="ghost"
1175
- size="sm"
1176
- onClick={() => setIsFullscreen(!isFullscreen)}
1177
- >
1178
- {isFullscreen ? (
1179
- <X className="h-4 w-4" />
1180
- ) : (
1181
- <Maximize2 className="h-4 w-4" />
1182
- )}
1183
- </Button>
1184
- </motion.div>
1185
- </CardHeader>
1186
-
1187
- <CardContent className="relative">
1188
- {/* Pan indicator */}
1189
- {enablePan && (
1190
- <motion.div
1191
- initial={{ opacity: 0 }}
1192
- animate={{ opacity: 1 }}
1193
- className="absolute top-2 left-2 z-10 flex items-center gap-1 text-xs text-muted-foreground bg-background/80 backdrop-blur-sm px-2 py-1 rounded-md"
1194
- >
1195
- <Move className="h-3 w-3" />
1196
- <span>Drag to pan</span>
1197
- </motion.div>
1198
- )}
1199
-
1200
- {/* Chart container */}
1201
- <motion.div
1202
- animate={{ scale: zoomLevel / 100 }}
1203
- transition={{ type: "spring", stiffness: 300, damping: 30 }}
1204
- style={{ transformOrigin: "center" }}
1205
- >
1206
- {responsive ? (
1207
- <ResponsiveContainer width="100%" height={sparklineMode ? 60 : height}>
1208
- {renderChart()}
1209
- </ResponsiveContainer>
1210
- ) : (
1211
- <div style={{ width: '100%', height: sparklineMode ? '60px' : `${height}px` }}>
1212
- {renderChart()}
1213
- </div>
1214
- )}
1215
- </motion.div>
1216
-
1217
- {/* Mini map */}
1218
- {showMiniMap && !sparklineMode && (
1219
- <motion.div
1220
- initial={{ opacity: 0, y: 20 }}
1221
- animate={{ opacity: 1, y: 0 }}
1222
- className="absolute bottom-2 right-2 w-32 h-20 bg-background/80 backdrop-blur-sm border rounded-lg p-1"
1223
- >
1224
- <ResponsiveContainer width="100%" height="100%">
1225
- <LineChart data={data} margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
1226
- {series.filter(s => !s.hide && !hiddenSeries.has(s.dataKey)).map((s, index) => (
1227
- <Line
1228
- key={s.dataKey}
1229
- type="monotone"
1230
- dataKey={s.dataKey}
1231
- stroke={s.color || themeColors[index % themeColors.length]}
1232
- strokeWidth={1}
1233
- dot={false}
1234
- />
1235
- ))}
1236
- </LineChart>
1237
- </ResponsiveContainer>
1238
- </motion.div>
1239
- )}
1240
- </CardContent>
1241
- </Card>
1242
- </motion.div>
1243
- )
1244
- }
1245
-
1246
- export default AdvancedChart