@moontra/moonui-pro 2.20.1 → 2.20.3

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 (162) hide show
  1. package/dist/index.d.ts +691 -261
  2. package/dist/index.mjs +7418 -4934
  3. package/package.json +11 -5
  4. package/plugin/index.d.ts +86 -0
  5. package/plugin/index.js +308 -0
  6. package/scripts/postbuild.js +27 -0
  7. package/scripts/postinstall.js +176 -23
  8. package/src/__tests__/use-intersection-observer.test.tsx +0 -216
  9. package/src/__tests__/use-local-storage.test.tsx +0 -174
  10. package/src/__tests__/use-pro-access.test.tsx +0 -183
  11. package/src/components/advanced-chart/advanced-chart.test.tsx +0 -281
  12. package/src/components/advanced-chart/index.tsx +0 -1242
  13. package/src/components/advanced-forms/index.tsx +0 -426
  14. package/src/components/animated-button/index.tsx +0 -385
  15. package/src/components/calendar/event-dialog.tsx +0 -372
  16. package/src/components/calendar/index.tsx +0 -1073
  17. package/src/components/calendar-pro/index.tsx +0 -1697
  18. package/src/components/color-picker/index.tsx +0 -432
  19. package/src/components/credit-card-input/index.tsx +0 -406
  20. package/src/components/dashboard/dashboard-grid.tsx +0 -462
  21. package/src/components/dashboard/demo.tsx +0 -425
  22. package/src/components/dashboard/index.tsx +0 -1046
  23. package/src/components/dashboard/time-range-picker.tsx +0 -336
  24. package/src/components/dashboard/types.ts +0 -222
  25. package/src/components/dashboard/widgets/activity-feed.tsx +0 -344
  26. package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
  27. package/src/components/dashboard/widgets/metric-card.tsx +0 -343
  28. package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
  29. package/src/components/data-table/data-table-column-toggle.tsx +0 -169
  30. package/src/components/data-table/data-table-export.ts +0 -156
  31. package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
  32. package/src/components/data-table/data-table.test.tsx +0 -187
  33. package/src/components/data-table/index.tsx +0 -845
  34. package/src/components/draggable-list/index.tsx +0 -100
  35. package/src/components/enhanced/badge.tsx +0 -191
  36. package/src/components/enhanced/button.tsx +0 -362
  37. package/src/components/enhanced/card.tsx +0 -266
  38. package/src/components/enhanced/dialog.tsx +0 -246
  39. package/src/components/enhanced/index.ts +0 -4
  40. package/src/components/error-boundary/index.tsx +0 -109
  41. package/src/components/file-upload/file-upload.test.tsx +0 -243
  42. package/src/components/file-upload/index.tsx +0 -1660
  43. package/src/components/floating-action-button/index.tsx +0 -206
  44. package/src/components/form-wizard/form-wizard-context.tsx +0 -307
  45. package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
  46. package/src/components/form-wizard/form-wizard-progress.tsx +0 -298
  47. package/src/components/form-wizard/form-wizard-step.tsx +0 -111
  48. package/src/components/form-wizard/index.tsx +0 -102
  49. package/src/components/form-wizard/types.ts +0 -76
  50. package/src/components/gesture-drawer/index.tsx +0 -551
  51. package/src/components/github-stars/github-api.ts +0 -426
  52. package/src/components/github-stars/hooks.ts +0 -516
  53. package/src/components/github-stars/index.tsx +0 -375
  54. package/src/components/github-stars/types.ts +0 -148
  55. package/src/components/github-stars/variants.tsx +0 -513
  56. package/src/components/health-check/index.tsx +0 -439
  57. package/src/components/hover-card-3d/index.tsx +0 -530
  58. package/src/components/index.ts +0 -128
  59. package/src/components/internal/index.ts +0 -78
  60. package/src/components/kanban/add-card-modal.tsx +0 -502
  61. package/src/components/kanban/card-detail-modal.tsx +0 -761
  62. package/src/components/kanban/index.ts +0 -13
  63. package/src/components/kanban/kanban.tsx +0 -1684
  64. package/src/components/kanban/types.ts +0 -168
  65. package/src/components/lazy-component/index.tsx +0 -823
  66. package/src/components/license-error/index.tsx +0 -29
  67. package/src/components/magnetic-button/index.tsx +0 -167
  68. package/src/components/memory-efficient-data/index.tsx +0 -1016
  69. package/src/components/moonui-quiz-form/index.tsx +0 -817
  70. package/src/components/optimized-image/index.tsx +0 -425
  71. package/src/components/performance-debugger/index.tsx +0 -589
  72. package/src/components/performance-monitor/index.tsx +0 -794
  73. package/src/components/phone-number-input/index.tsx +0 -338
  74. package/src/components/pinch-zoom/index.tsx +0 -566
  75. package/src/components/quiz-form/index.tsx +0 -479
  76. package/src/components/rich-text-editor/index-old-backup.tsx +0 -437
  77. package/src/components/rich-text-editor/index.tsx +0 -2324
  78. package/src/components/rich-text-editor/slash-commands-extension.ts +0 -220
  79. package/src/components/rich-text-editor/slash-commands.css +0 -35
  80. package/src/components/rich-text-editor/table-styles.css +0 -65
  81. package/src/components/sidebar/index.tsx +0 -865
  82. package/src/components/spotlight-card/index.tsx +0 -191
  83. package/src/components/swipeable-card/index.tsx +0 -100
  84. package/src/components/timeline/index.tsx +0 -1148
  85. package/src/components/ui/accordion.tsx +0 -73
  86. package/src/components/ui/alert-dialog.tsx +0 -141
  87. package/src/components/ui/alert.tsx +0 -141
  88. package/src/components/ui/aspect-ratio.tsx +0 -245
  89. package/src/components/ui/avatar.tsx +0 -153
  90. package/src/components/ui/badge.tsx +0 -228
  91. package/src/components/ui/breadcrumb.tsx +0 -214
  92. package/src/components/ui/button.tsx +0 -222
  93. package/src/components/ui/calendar.tsx +0 -387
  94. package/src/components/ui/card.tsx +0 -214
  95. package/src/components/ui/checkbox.tsx +0 -259
  96. package/src/components/ui/collapsible.tsx +0 -135
  97. package/src/components/ui/color-picker.tsx +0 -97
  98. package/src/components/ui/command.tsx +0 -225
  99. package/src/components/ui/dialog.tsx +0 -334
  100. package/src/components/ui/dropdown-menu.tsx +0 -218
  101. package/src/components/ui/gesture-drawer.tsx +0 -11
  102. package/src/components/ui/hover-card.tsx +0 -29
  103. package/src/components/ui/index.ts +0 -190
  104. package/src/components/ui/input.tsx +0 -222
  105. package/src/components/ui/label.tsx +0 -29
  106. package/src/components/ui/lightbox.tsx +0 -606
  107. package/src/components/ui/magnetic-button.tsx +0 -129
  108. package/src/components/ui/media-gallery.tsx +0 -612
  109. package/src/components/ui/pagination.tsx +0 -123
  110. package/src/components/ui/popover.tsx +0 -185
  111. package/src/components/ui/progress.tsx +0 -30
  112. package/src/components/ui/radio-group.tsx +0 -257
  113. package/src/components/ui/scroll-area.tsx +0 -47
  114. package/src/components/ui/select.tsx +0 -374
  115. package/src/components/ui/separator.tsx +0 -145
  116. package/src/components/ui/sheet.tsx +0 -139
  117. package/src/components/ui/skeleton.tsx +0 -20
  118. package/src/components/ui/slider.tsx +0 -354
  119. package/src/components/ui/spotlight-card.tsx +0 -119
  120. package/src/components/ui/switch.tsx +0 -86
  121. package/src/components/ui/table.tsx +0 -329
  122. package/src/components/ui/tabs.tsx +0 -198
  123. package/src/components/ui/textarea.tsx +0 -28
  124. package/src/components/ui/toast.tsx +0 -317
  125. package/src/components/ui/toggle.tsx +0 -119
  126. package/src/components/ui/tooltip.tsx +0 -151
  127. package/src/components/virtual-list/index.tsx +0 -668
  128. package/src/hooks/use-chart.ts +0 -205
  129. package/src/hooks/use-data-table.ts +0 -182
  130. package/src/hooks/use-docs-pro-access.ts +0 -13
  131. package/src/hooks/use-license-check.ts +0 -65
  132. package/src/hooks/use-subscription.ts +0 -19
  133. package/src/hooks/use-toast.ts +0 -15
  134. package/src/index.ts +0 -14
  135. package/src/lib/ai-providers.ts +0 -377
  136. package/src/lib/component-metadata.ts +0 -18
  137. package/src/lib/micro-interactions.ts +0 -255
  138. package/src/lib/paddle.ts +0 -17
  139. package/src/lib/utils.ts +0 -6
  140. package/src/patterns/login-form/index.tsx +0 -276
  141. package/src/patterns/login-form/types.ts +0 -67
  142. package/src/setupTests.ts +0 -41
  143. package/src/styles/advanced-chart.css +0 -239
  144. package/src/styles/calendar.css +0 -35
  145. package/src/styles/design-system.css +0 -363
  146. package/src/styles/index.css +0 -85
  147. package/src/styles/tailwind.css +0 -7
  148. package/src/styles/tokens.css +0 -455
  149. package/src/types/moonui.d.ts +0 -22
  150. package/src/types/next-auth.d.ts +0 -21
  151. package/src/use-intersection-observer.tsx +0 -154
  152. package/src/use-local-storage.tsx +0 -71
  153. package/src/use-paddle.ts +0 -138
  154. package/src/use-performance-optimizer.ts +0 -389
  155. package/src/use-pro-access.ts +0 -141
  156. package/src/use-scroll-animation.ts +0 -219
  157. package/src/use-subscription.ts +0 -37
  158. package/src/use-toast.ts +0 -32
  159. package/src/utils/chart-helpers.ts +0 -357
  160. package/src/utils/cn.ts +0 -6
  161. package/src/utils/data-processing.ts +0 -151
  162. package/src/utils/license-validator.tsx +0 -183
@@ -1,1242 +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
- return (
779
- <circle
780
- {...props}
781
- r={isHovered ? 6 : 4}
782
- className="transition-all duration-200 hover:r-6"
783
- onMouseEnter={() => setHoveredDataPoint(props.payload)}
784
- onMouseLeave={() => setHoveredDataPoint(null)}
785
- />
786
- )
787
- }}
788
- />
789
- )
790
- })}
791
- </ScatterChart>
792
- )
793
-
794
- case 'radar':
795
- return (
796
- <RadarChart {...commonProps}>
797
- <PolarGrid
798
- stroke={darkMode ? '#374151' : '#e5e7eb'}
799
- strokeDasharray="3 3"
800
- />
801
- <PolarAngleAxis
802
- dataKey={xAxisKey}
803
- {...axisStyle}
804
- tick={{ fontSize: 10 }}
805
- />
806
- <PolarRadiusAxis
807
- {...axisStyle}
808
- tick={{ fontSize: 10 }}
809
- axisLine={false}
810
- />
811
- {showTooltip && (
812
- <Tooltip
813
- /* @ts-ignore */
814
- content={customTooltip ? customTooltip : CustomTooltip}
815
- />
816
- )}
817
- {showLegend && (
818
- <Legend
819
- content={(props) => {
820
- const CustomLegendComponent = customLegend
821
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
822
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
823
- }}
824
- />
825
- )}
826
- {visibleSeries.map((s, index) => {
827
- const color = s.color || themeColors[index % themeColors.length]
828
- return (
829
- <Radar
830
- key={s.dataKey}
831
- dataKey={s.dataKey}
832
- stroke={color}
833
- fill={color}
834
- fillOpacity={0.3}
835
- name={s.name}
836
- animationDuration={animated ? animationDuration : 0}
837
- animationBegin={index * 100}
838
- />
839
- )
840
- })}
841
- </RadarChart>
842
- )
843
-
844
- case 'radialBar':
845
- return (
846
- <RadialBarChart
847
- {...commonProps}
848
- innerRadius="10%"
849
- outerRadius="90%"
850
- >
851
- {showTooltip && (
852
- <Tooltip
853
- /* @ts-ignore */
854
- content={customTooltip ? customTooltip : CustomTooltip}
855
- />
856
- )}
857
- {showLegend && (
858
- <Legend
859
- content={(props) => {
860
- const CustomLegendComponent = customLegend
861
- return CustomLegendComponent ? <CustomLegendComponent {...props} /> :
862
- <CustomLegend {...props} onItemClick={handleLegendItemClick} />
863
- }}
864
- />
865
- )}
866
- <RadialBar
867
- dataKey={series[0]?.dataKey || 'value'}
868
- cornerRadius={10}
869
- fill={themeColors[0]}
870
- animationDuration={animated ? animationDuration : 0}
871
- label={showDataLabels ? {
872
- fill: darkMode ? '#e5e7eb' : '#374151',
873
- position: 'center'
874
- } : false}
875
- />
876
- </RadialBarChart>
877
- )
878
-
879
- default:
880
- return <div>Unsupported chart type</div>
881
- }
882
- }
883
-
884
- if (loading) {
885
- return (
886
- <Card className={cn("w-full overflow-hidden", className)}>
887
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
888
- <div className="space-y-1">
889
- <Skeleton className="h-4 w-32" />
890
- <Skeleton className="h-3 w-48" />
891
- </div>
892
- <div className="flex items-center space-x-1">
893
- <Skeleton className="h-8 w-8 rounded" />
894
- <Skeleton className="h-8 w-8 rounded" />
895
- <Skeleton className="h-8 w-8 rounded" />
896
- </div>
897
- </CardHeader>
898
- <CardContent>
899
- <ChartSkeleton height={height} />
900
- </CardContent>
901
- </Card>
902
- )
903
- }
904
-
905
- if (error) {
906
- return (
907
- <Card className={cn("w-full", className)}>
908
- <CardContent className="flex items-center justify-center" style={{ height }}>
909
- <motion.div
910
- initial={{ opacity: 0, scale: 0.9 }}
911
- animate={{ opacity: 1, scale: 1 }}
912
- className="text-center"
913
- >
914
- <div className="w-12 h-12 rounded-full bg-destructive/10 dark:bg-destructive/20 flex items-center justify-center mx-auto mb-4">
915
- <X className="w-6 h-6 text-destructive" />
916
- </div>
917
- <p className="text-destructive mb-4 font-medium">{error}</p>
918
- {onRefresh && (
919
- <Button
920
- variant="outline"
921
- onClick={onRefresh}
922
- className="group"
923
- >
924
- <RefreshCw className="mr-2 h-4 w-4 group-hover:rotate-180 transition-transform duration-500" />
925
- Retry
926
- </Button>
927
- )}
928
- </motion.div>
929
- </CardContent>
930
- </Card>
931
- )
932
- }
933
-
934
- return (
935
- <motion.div
936
- ref={containerRef}
937
- className={cn(
938
- "relative",
939
- isFullscreen && "fixed inset-0 z-50 bg-background p-4"
940
- )}
941
- initial={{ opacity: 0, y: 20 }}
942
- animate={{ opacity: 1, y: 0 }}
943
- transition={{ duration: 0.5 }}
944
- >
945
- <Card className={cn(
946
- "w-full overflow-hidden transition-all duration-300",
947
- darkMode && "dark",
948
- isFullscreen && "h-full",
949
- className
950
- )} ref={chartRef}>
951
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
952
- <motion.div
953
- className="space-y-1"
954
- initial={{ opacity: 0, x: -20 }}
955
- animate={{ opacity: 1, x: 0 }}
956
- transition={{ delay: 0.1 }}
957
- >
958
- <CardTitle className="text-base font-medium flex items-center gap-2">
959
- {title}
960
- {trend && (
961
- <motion.span
962
- className="inline-flex items-center"
963
- initial={{ scale: 0 }}
964
- animate={{ scale: 1 }}
965
- transition={{ type: "spring", delay: 0.3 }}
966
- >
967
- {trend.direction === 'up' && (
968
- <TrendingUp className="h-4 w-4 text-green-500 dark:text-green-400" />
969
- )}
970
- {trend.direction === 'down' && (
971
- <TrendingDown className="h-4 w-4 text-red-500 dark:text-red-400" />
972
- )}
973
- {trend.direction === 'neutral' && (
974
- <Minus className="h-4 w-4 text-muted-foreground" />
975
- )}
976
- <span className={cn(
977
- "ml-1 text-sm font-semibold",
978
- trend.direction === 'up' && "text-green-500 dark:text-green-400",
979
- trend.direction === 'down' && "text-red-500 dark:text-red-400",
980
- trend.direction === 'neutral' && "text-muted-foreground"
981
- )}>
982
- {trend.percentage}%
983
- </span>
984
- </motion.span>
985
- )}
986
- {sparklineMode && series[0] && (
987
- <SparklinePreview
988
- data={data}
989
- dataKey={series[0].dataKey}
990
- color={series[0].color || themeColors[0]}
991
- />
992
- )}
993
- </CardTitle>
994
- {subtitle && (
995
- <p className="text-xs text-muted-foreground">{subtitle}</p>
996
- )}
997
- </motion.div>
998
-
999
- <motion.div
1000
- className="flex items-center space-x-1"
1001
- initial={{ opacity: 0, x: 20 }}
1002
- animate={{ opacity: 1, x: 0 }}
1003
- transition={{ delay: 0.2 }}
1004
- >
1005
- {/* Zoom controls */}
1006
- {enableZoom && !sparklineMode && (
1007
- <div className="flex items-center border rounded-lg mr-2">
1008
- <Button
1009
- variant="ghost"
1010
- size="sm"
1011
- onClick={() => handleZoom('out')}
1012
- disabled={zoomLevel <= 50}
1013
- className="h-7 w-7 p-0"
1014
- >
1015
- <ZoomOut className="h-3 w-3" />
1016
- </Button>
1017
- <span className="text-xs px-2 text-muted-foreground">
1018
- {zoomLevel}%
1019
- </span>
1020
- <Button
1021
- variant="ghost"
1022
- size="sm"
1023
- onClick={() => handleZoom('in')}
1024
- disabled={zoomLevel >= 200}
1025
- className="h-7 w-7 p-0"
1026
- >
1027
- <ZoomIn className="h-3 w-3" />
1028
- </Button>
1029
- </div>
1030
- )}
1031
-
1032
- {/* Other controls */}
1033
- {onRefresh && (
1034
- <Button
1035
- variant="ghost"
1036
- size="sm"
1037
- onClick={onRefresh}
1038
- className="group"
1039
- >
1040
- <RefreshCw className="h-4 w-4 group-hover:rotate-180 transition-transform duration-500" />
1041
- </Button>
1042
- )}
1043
-
1044
- {/* Settings popover */}
1045
- <Popover open={showSettings} onOpenChange={setShowSettings}>
1046
- <PopoverTrigger asChild>
1047
- <Button variant="ghost" size="sm">
1048
- <Settings className="h-4 w-4" />
1049
- </Button>
1050
- </PopoverTrigger>
1051
- <PopoverContent className="w-80" align="end">
1052
- <div className="space-y-4">
1053
- <div className="space-y-2">
1054
- <h4 className="font-medium text-sm">Chart Settings</h4>
1055
- </div>
1056
-
1057
- {/* Theme selector */}
1058
- <div className="space-y-2">
1059
- <Label className="text-xs">Color Theme</Label>
1060
- <div className="grid grid-cols-3 gap-2">
1061
- {Object.entries(COLOR_THEMES).map(([themeName, colors]) => (
1062
- <button
1063
- key={themeName}
1064
- onClick={() => setSelectedTheme(themeName as any)}
1065
- className={cn(
1066
- "p-2 rounded-lg border-2 transition-all",
1067
- selectedTheme === themeName
1068
- ? "border-primary"
1069
- : "border-transparent hover:border-muted-foreground/20"
1070
- )}
1071
- >
1072
- <div className="flex gap-1">
1073
- {colors.slice(0, 3).map((color, i) => (
1074
- <div
1075
- key={i}
1076
- className="w-3 h-3 rounded-full"
1077
- style={{ backgroundColor: color }}
1078
- />
1079
- ))}
1080
- </div>
1081
- <span className="text-xs mt-1 block capitalize">
1082
- {themeName}
1083
- </span>
1084
- </button>
1085
- ))}
1086
- </div>
1087
- </div>
1088
-
1089
- {/* Animation toggle */}
1090
- <div className="flex items-center justify-between">
1091
- <Label htmlFor="animation" className="text-xs">
1092
- Animations
1093
- </Label>
1094
- <Switch
1095
- id="animation"
1096
- checked={animated}
1097
- onCheckedChange={(checked) => {
1098
- // Update animated prop
1099
- }}
1100
- />
1101
- </div>
1102
-
1103
- {/* Grid toggle */}
1104
- <div className="flex items-center justify-between">
1105
- <Label htmlFor="grid" className="text-xs">
1106
- Show Grid
1107
- </Label>
1108
- <Switch
1109
- id="grid"
1110
- checked={showGrid}
1111
- onCheckedChange={(checked) => {
1112
- // Update showGrid prop
1113
- }}
1114
- />
1115
- </div>
1116
-
1117
- {/* Crosshair toggle */}
1118
- <div className="flex items-center justify-between">
1119
- <Label htmlFor="crosshair" className="text-xs">
1120
- Crosshair Cursor
1121
- </Label>
1122
- <Switch
1123
- id="crosshair"
1124
- checked={showCrosshair}
1125
- onCheckedChange={(checked) => {
1126
- // Update showCrosshair prop
1127
- }}
1128
- />
1129
- </div>
1130
- </div>
1131
- </PopoverContent>
1132
- </Popover>
1133
-
1134
- {/* Export menu */}
1135
- {onExport && (
1136
- <DropdownMenu>
1137
- <DropdownMenuTrigger asChild>
1138
- <Button variant="ghost" size="sm">
1139
- <Download className="h-4 w-4" />
1140
- </Button>
1141
- </DropdownMenuTrigger>
1142
- <DropdownMenuContent align="end">
1143
- <DropdownMenuItem onClick={() => handleExport('png')}>
1144
- <Image className="mr-2 h-4 w-4" />
1145
- Export as PNG
1146
- </DropdownMenuItem>
1147
- <DropdownMenuItem onClick={() => handleExport('svg')}>
1148
- <FileSpreadsheet className="mr-2 h-4 w-4" />
1149
- Export as SVG
1150
- </DropdownMenuItem>
1151
- <DropdownMenuItem onClick={() => handleExport('pdf')}>
1152
- <FileSpreadsheet className="mr-2 h-4 w-4" />
1153
- Export as PDF
1154
- </DropdownMenuItem>
1155
- <DropdownMenuSeparator />
1156
- <DropdownMenuItem onClick={() => handleExport('csv')}>
1157
- <FileSpreadsheet className="mr-2 h-4 w-4" />
1158
- Export as CSV
1159
- </DropdownMenuItem>
1160
- <DropdownMenuItem onClick={() => handleExport('json')}>
1161
- <FileJson className="mr-2 h-4 w-4" />
1162
- Export as JSON
1163
- </DropdownMenuItem>
1164
- </DropdownMenuContent>
1165
- </DropdownMenu>
1166
- )}
1167
-
1168
- {/* Fullscreen toggle */}
1169
- <Button
1170
- variant="ghost"
1171
- size="sm"
1172
- onClick={() => setIsFullscreen(!isFullscreen)}
1173
- >
1174
- {isFullscreen ? (
1175
- <X className="h-4 w-4" />
1176
- ) : (
1177
- <Maximize2 className="h-4 w-4" />
1178
- )}
1179
- </Button>
1180
- </motion.div>
1181
- </CardHeader>
1182
-
1183
- <CardContent className="relative">
1184
- {/* Pan indicator */}
1185
- {enablePan && (
1186
- <motion.div
1187
- initial={{ opacity: 0 }}
1188
- animate={{ opacity: 1 }}
1189
- 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"
1190
- >
1191
- <Move className="h-3 w-3" />
1192
- <span>Drag to pan</span>
1193
- </motion.div>
1194
- )}
1195
-
1196
- {/* Chart container */}
1197
- <motion.div
1198
- animate={{ scale: zoomLevel / 100 }}
1199
- transition={{ type: "spring", stiffness: 300, damping: 30 }}
1200
- style={{ transformOrigin: "center" }}
1201
- >
1202
- {responsive ? (
1203
- <ResponsiveContainer width="100%" height={sparklineMode ? 60 : height}>
1204
- {renderChart()}
1205
- </ResponsiveContainer>
1206
- ) : (
1207
- <div style={{ width: '100%', height: sparklineMode ? '60px' : `${height}px` }}>
1208
- {renderChart()}
1209
- </div>
1210
- )}
1211
- </motion.div>
1212
-
1213
- {/* Mini map */}
1214
- {showMiniMap && !sparklineMode && (
1215
- <motion.div
1216
- initial={{ opacity: 0, y: 20 }}
1217
- animate={{ opacity: 1, y: 0 }}
1218
- className="absolute bottom-2 right-2 w-32 h-20 bg-background/80 backdrop-blur-sm border rounded-lg p-1"
1219
- >
1220
- <ResponsiveContainer width="100%" height="100%">
1221
- <LineChart data={data} margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
1222
- {series.filter(s => !s.hide && !hiddenSeries.has(s.dataKey)).map((s, index) => (
1223
- <Line
1224
- key={s.dataKey}
1225
- type="monotone"
1226
- dataKey={s.dataKey}
1227
- stroke={s.color || themeColors[index % themeColors.length]}
1228
- strokeWidth={1}
1229
- dot={false}
1230
- />
1231
- ))}
1232
- </LineChart>
1233
- </ResponsiveContainer>
1234
- </motion.div>
1235
- )}
1236
- </CardContent>
1237
- </Card>
1238
- </motion.div>
1239
- )
1240
- }
1241
-
1242
- export default AdvancedChart