@moontra/moonui-pro 2.20.2 → 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.
- package/package.json +8 -3
- package/plugin/index.d.ts +86 -0
- package/plugin/index.js +308 -0
- package/scripts/postinstall.js +176 -23
- package/src/components/advanced-chart/index.tsx +0 -1246
- package/src/components/advanced-forms/index.tsx +0 -585
- package/src/components/animated-button/index.tsx +0 -385
- package/src/components/calendar/event-dialog.tsx +0 -377
- package/src/components/calendar/index.tsx +0 -1220
- package/src/components/calendar-pro/index.tsx +0 -1697
- package/src/components/color-picker/index.tsx +0 -432
- package/src/components/credit-card-input/index.tsx +0 -406
- package/src/components/dashboard/dashboard-grid.tsx +0 -480
- package/src/components/dashboard/demo.tsx +0 -425
- package/src/components/dashboard/index.tsx +0 -1046
- package/src/components/dashboard/time-range-picker.tsx +0 -336
- package/src/components/dashboard/types.ts +0 -225
- package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
- package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
- package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
- package/src/components/dashboard/widgets/index.ts +0 -5
- package/src/components/dashboard/widgets/metric-card.tsx +0 -363
- package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
- package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
- package/src/components/data-table/data-table-column-toggle.tsx +0 -169
- package/src/components/data-table/data-table-export.ts +0 -156
- package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
- package/src/components/data-table/index.tsx +0 -845
- package/src/components/draggable-list/index.tsx +0 -100
- package/src/components/error-boundary/index.tsx +0 -232
- package/src/components/file-upload/index.tsx +0 -1660
- package/src/components/floating-action-button/index.tsx +0 -206
- package/src/components/form-wizard/form-wizard-context.tsx +0 -335
- package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
- package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
- package/src/components/form-wizard/form-wizard-step.tsx +0 -111
- package/src/components/form-wizard/index.tsx +0 -102
- package/src/components/form-wizard/types.ts +0 -77
- package/src/components/gesture-drawer/index.tsx +0 -551
- package/src/components/github-stars/github-api.ts +0 -426
- package/src/components/github-stars/hooks.ts +0 -517
- package/src/components/github-stars/index.tsx +0 -375
- package/src/components/github-stars/types.ts +0 -148
- package/src/components/github-stars/variants.tsx +0 -515
- package/src/components/health-check/index.tsx +0 -439
- package/src/components/hover-card-3d/index.tsx +0 -529
- package/src/components/index.ts +0 -130
- package/src/components/internal/index.ts +0 -78
- package/src/components/kanban/add-card-modal.tsx +0 -502
- package/src/components/kanban/card-detail-modal.tsx +0 -761
- package/src/components/kanban/index.ts +0 -13
- package/src/components/kanban/kanban.tsx +0 -1689
- package/src/components/kanban/types.ts +0 -168
- package/src/components/lazy-component/index.tsx +0 -823
- package/src/components/license-error/index.tsx +0 -31
- package/src/components/magnetic-button/index.tsx +0 -216
- package/src/components/memory-efficient-data/index.tsx +0 -1018
- package/src/components/moonui-quiz-form/index.tsx +0 -817
- package/src/components/navbar/index.tsx +0 -781
- package/src/components/optimized-image/index.tsx +0 -425
- package/src/components/performance-debugger/index.tsx +0 -613
- package/src/components/performance-monitor/index.tsx +0 -808
- package/src/components/phone-number-input/index.tsx +0 -343
- package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
- package/src/components/pinch-zoom/index.tsx +0 -566
- package/src/components/quiz-form/index.tsx +0 -479
- package/src/components/rich-text-editor/index.tsx +0 -2322
- package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
- package/src/components/rich-text-editor/slash-commands.css +0 -35
- package/src/components/rich-text-editor/table-styles.css +0 -65
- package/src/components/sidebar/index.tsx +0 -884
- package/src/components/spotlight-card/index.tsx +0 -191
- package/src/components/swipeable-card/index.tsx +0 -100
- package/src/components/timeline/index.tsx +0 -1183
- package/src/components/ui/accordion.tsx +0 -581
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/alert.tsx +0 -141
- package/src/components/ui/aspect-ratio.tsx +0 -245
- package/src/components/ui/avatar.tsx +0 -155
- package/src/components/ui/badge.tsx +0 -230
- package/src/components/ui/breadcrumb.tsx +0 -216
- package/src/components/ui/button.tsx +0 -228
- package/src/components/ui/calendar.tsx +0 -387
- package/src/components/ui/card.tsx +0 -216
- package/src/components/ui/checkbox.tsx +0 -259
- package/src/components/ui/collapsible.tsx +0 -631
- package/src/components/ui/color-picker.tsx +0 -97
- package/src/components/ui/command.tsx +0 -948
- package/src/components/ui/dialog.tsx +0 -752
- package/src/components/ui/dropdown-menu.tsx +0 -706
- package/src/components/ui/gesture-drawer.tsx +0 -11
- package/src/components/ui/hover-card.tsx +0 -29
- package/src/components/ui/index.ts +0 -222
- package/src/components/ui/input.tsx +0 -224
- package/src/components/ui/label.tsx +0 -29
- package/src/components/ui/lightbox.tsx +0 -606
- package/src/components/ui/magnetic-button.tsx +0 -129
- package/src/components/ui/media-gallery.tsx +0 -611
- package/src/components/ui/navigation-menu.tsx +0 -130
- package/src/components/ui/pagination.tsx +0 -125
- package/src/components/ui/popover.tsx +0 -185
- package/src/components/ui/progress.tsx +0 -30
- package/src/components/ui/radio-group.tsx +0 -257
- package/src/components/ui/scroll-area.tsx +0 -47
- package/src/components/ui/select.tsx +0 -378
- package/src/components/ui/separator.tsx +0 -145
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/skeleton.tsx +0 -20
- package/src/components/ui/slider.tsx +0 -354
- package/src/components/ui/spotlight-card.tsx +0 -119
- package/src/components/ui/switch.tsx +0 -86
- package/src/components/ui/table.tsx +0 -331
- package/src/components/ui/tabs-pro.tsx +0 -542
- package/src/components/ui/tabs.tsx +0 -54
- package/src/components/ui/textarea.tsx +0 -28
- package/src/components/ui/toast.tsx +0 -317
- package/src/components/ui/toggle.tsx +0 -119
- package/src/components/ui/tooltip.tsx +0 -151
- package/src/components/virtual-list/index.tsx +0 -668
- package/src/hooks/use-chart.ts +0 -205
- package/src/hooks/use-data-table.ts +0 -182
- package/src/hooks/use-docs-pro-access.ts +0 -13
- package/src/hooks/use-license-check.ts +0 -65
- package/src/hooks/use-subscription.ts +0 -19
- package/src/hooks/use-toast.ts +0 -15
- package/src/index.ts +0 -22
- package/src/lib/ai-providers.ts +0 -377
- package/src/lib/component-metadata.ts +0 -18
- package/src/lib/micro-interactions.ts +0 -255
- package/src/lib/paddle.ts +0 -17
- package/src/lib/utils.ts +0 -6
- package/src/patterns/login-form/index.tsx +0 -276
- package/src/patterns/login-form/types.ts +0 -67
- package/src/setupTests.ts +0 -41
- package/src/styles/advanced-chart.css +0 -239
- package/src/styles/calendar.css +0 -35
- package/src/styles/design-system.css +0 -363
- package/src/styles/index.css +0 -681
- package/src/styles/tailwind.css +0 -7
- package/src/styles/tokens.css +0 -455
- package/src/types/next-auth.d.ts +0 -21
- package/src/use-intersection-observer.tsx +0 -154
- package/src/use-local-storage.tsx +0 -71
- package/src/use-paddle.ts +0 -138
- package/src/use-performance-optimizer.ts +0 -389
- package/src/use-pro-access.ts +0 -141
- package/src/use-scroll-animation.ts +0 -219
- package/src/use-subscription.ts +0 -37
- package/src/use-toast.ts +0 -32
- package/src/utils/chart-helpers.ts +0 -357
- package/src/utils/cn.ts +0 -6
- package/src/utils/data-processing.ts +0 -151
- package/src/utils/license-validator.tsx +0 -183
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useEffect, useRef, useState } from 'react'
|
|
4
|
-
import { useInView, useScroll, useTransform } from 'framer-motion'
|
|
5
|
-
|
|
6
|
-
interface UseScrollAnimationOptions {
|
|
7
|
-
threshold?: number
|
|
8
|
-
triggerOnce?: boolean
|
|
9
|
-
rootMargin?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function useScrollAnimation(options: UseScrollAnimationOptions = {}) {
|
|
13
|
-
const ref = useRef<HTMLElement>(null)
|
|
14
|
-
const isInView = useInView(ref, {
|
|
15
|
-
amount: options.threshold || 0.1,
|
|
16
|
-
once: options.triggerOnce ?? true,
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
return { ref, isInView }
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function useScrollProgress() {
|
|
23
|
-
const { scrollYProgress } = useScroll()
|
|
24
|
-
return scrollYProgress
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function useScrollBasedAnimation() {
|
|
28
|
-
const { scrollY } = useScroll()
|
|
29
|
-
const y = useTransform(scrollY, [0, 300], [0, -50])
|
|
30
|
-
const opacity = useTransform(scrollY, [0, 300], [1, 0])
|
|
31
|
-
const scale = useTransform(scrollY, [0, 300], [1, 0.8])
|
|
32
|
-
|
|
33
|
-
return { y, opacity, scale, scrollY }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function useParallaxScroll(speed: number = 0.5) {
|
|
37
|
-
const ref = useRef<HTMLElement>(null)
|
|
38
|
-
const { scrollYProgress } = useScroll({
|
|
39
|
-
target: ref,
|
|
40
|
-
offset: ['start end', 'end start']
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const y = useTransform(scrollYProgress, [0, 1], [`-${speed * 100}%`, `${speed * 100}%`])
|
|
44
|
-
|
|
45
|
-
return { ref, y }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function useScrollDirection() {
|
|
49
|
-
const [scrollDirection, setScrollDirection] = useState<'up' | 'down'>('down')
|
|
50
|
-
const [scrollY, setScrollY] = useState(0)
|
|
51
|
-
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
let ticking = false
|
|
54
|
-
|
|
55
|
-
const updateScrollDirection = () => {
|
|
56
|
-
const newScrollY = window.scrollY
|
|
57
|
-
|
|
58
|
-
if (Math.abs(newScrollY - scrollY) < 5) {
|
|
59
|
-
ticking = false
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
setScrollDirection(newScrollY > scrollY ? 'down' : 'up')
|
|
64
|
-
setScrollY(newScrollY)
|
|
65
|
-
ticking = false
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const handleScroll = () => {
|
|
69
|
-
if (!ticking) {
|
|
70
|
-
requestAnimationFrame(updateScrollDirection)
|
|
71
|
-
ticking = true
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
window.addEventListener('scroll', handleScroll)
|
|
76
|
-
return () => window.removeEventListener('scroll', handleScroll)
|
|
77
|
-
}, [scrollY])
|
|
78
|
-
|
|
79
|
-
return { scrollDirection, scrollY }
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function useScrollToElement() {
|
|
83
|
-
const scrollToElement = (elementId: string, offset: number = 0) => {
|
|
84
|
-
const element = document.getElementById(elementId)
|
|
85
|
-
if (element) {
|
|
86
|
-
const elementPosition = element.offsetTop - offset
|
|
87
|
-
window.scrollTo({
|
|
88
|
-
top: elementPosition,
|
|
89
|
-
behavior: 'smooth'
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return { scrollToElement }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
interface UseInfiniteScrollOptions {
|
|
98
|
-
threshold?: number
|
|
99
|
-
rootMargin?: string
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function useInfiniteScroll(
|
|
103
|
-
callback: () => void,
|
|
104
|
-
options: UseInfiniteScrollOptions = {}
|
|
105
|
-
) {
|
|
106
|
-
const ref = useRef<HTMLElement>(null)
|
|
107
|
-
const isInView = useInView(ref, {
|
|
108
|
-
amount: options.threshold || 0.1,
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
useEffect(() => {
|
|
112
|
-
if (isInView) {
|
|
113
|
-
callback()
|
|
114
|
-
}
|
|
115
|
-
}, [isInView, callback])
|
|
116
|
-
|
|
117
|
-
return ref
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function useScrollBasedScale() {
|
|
121
|
-
const ref = useRef<HTMLElement>(null)
|
|
122
|
-
const { scrollYProgress } = useScroll({
|
|
123
|
-
target: ref,
|
|
124
|
-
offset: ['start end', 'end start']
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
const scale = useTransform(scrollYProgress, [0, 0.5, 1], [0.8, 1, 0.8])
|
|
128
|
-
const opacity = useTransform(scrollYProgress, [0, 0.2, 0.8, 1], [0, 1, 1, 0])
|
|
129
|
-
|
|
130
|
-
return { ref, scale, opacity }
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function useScrollBasedRotation() {
|
|
134
|
-
const ref = useRef<HTMLElement>(null)
|
|
135
|
-
const { scrollYProgress } = useScroll({
|
|
136
|
-
target: ref,
|
|
137
|
-
offset: ['start end', 'end start']
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
const rotate = useTransform(scrollYProgress, [0, 1], [0, 360])
|
|
141
|
-
|
|
142
|
-
return { ref, rotate }
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function useScrollTriggeredCounter(
|
|
146
|
-
endValue: number,
|
|
147
|
-
duration: number = 2000
|
|
148
|
-
) {
|
|
149
|
-
const [count, setCount] = useState(0)
|
|
150
|
-
const [isVisible, setIsVisible] = useState(false)
|
|
151
|
-
const ref = useRef<HTMLElement>(null)
|
|
152
|
-
|
|
153
|
-
const isInView = useInView(ref, {
|
|
154
|
-
amount: 0.5,
|
|
155
|
-
once: true
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
useEffect(() => {
|
|
159
|
-
if (isInView && !isVisible) {
|
|
160
|
-
setIsVisible(true)
|
|
161
|
-
let startTime: number
|
|
162
|
-
|
|
163
|
-
const animate = (currentTime: number) => {
|
|
164
|
-
if (!startTime) startTime = currentTime
|
|
165
|
-
const elapsed = currentTime - startTime
|
|
166
|
-
const progress = Math.min(elapsed / duration, 1)
|
|
167
|
-
|
|
168
|
-
// Easing function for smooth animation
|
|
169
|
-
const easeOutQuart = 1 - Math.pow(1 - progress, 4)
|
|
170
|
-
setCount(Math.floor(easeOutQuart * endValue))
|
|
171
|
-
|
|
172
|
-
if (progress < 1) {
|
|
173
|
-
requestAnimationFrame(animate)
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
requestAnimationFrame(animate)
|
|
178
|
-
}
|
|
179
|
-
}, [isInView, endValue, duration, isVisible])
|
|
180
|
-
|
|
181
|
-
return { ref, count }
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function useScrollBasedBlur() {
|
|
185
|
-
const { scrollY } = useScroll()
|
|
186
|
-
const blur = useTransform(scrollY, [0, 300], [0, 10])
|
|
187
|
-
|
|
188
|
-
return blur
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function useScrollSnapPoints(snapPoints: number[]) {
|
|
192
|
-
const [currentSnap, setCurrentSnap] = useState(0)
|
|
193
|
-
|
|
194
|
-
useEffect(() => {
|
|
195
|
-
const handleScroll = () => {
|
|
196
|
-
const scrollPosition = window.scrollY
|
|
197
|
-
const closest = snapPoints.reduce((prev, curr, index) => {
|
|
198
|
-
return Math.abs(curr - scrollPosition) < Math.abs(snapPoints[prev] - scrollPosition)
|
|
199
|
-
? index
|
|
200
|
-
: prev
|
|
201
|
-
}, 0)
|
|
202
|
-
setCurrentSnap(closest)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
window.addEventListener('scroll', handleScroll)
|
|
206
|
-
return () => window.removeEventListener('scroll', handleScroll)
|
|
207
|
-
}, [snapPoints])
|
|
208
|
-
|
|
209
|
-
const scrollToSnap = (index: number) => {
|
|
210
|
-
if (index >= 0 && index < snapPoints.length) {
|
|
211
|
-
window.scrollTo({
|
|
212
|
-
top: snapPoints[index],
|
|
213
|
-
behavior: 'smooth'
|
|
214
|
-
})
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return { currentSnap, scrollToSnap }
|
|
219
|
-
}
|
package/src/use-subscription.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { useSession } from "next-auth/react";
|
|
2
|
-
|
|
3
|
-
export function useSubscription() {
|
|
4
|
-
const { data: session, status } = useSession();
|
|
5
|
-
|
|
6
|
-
const isLoading = status === "loading";
|
|
7
|
-
const isAuthenticated = status === "authenticated";
|
|
8
|
-
|
|
9
|
-
// Admin kullanıcılar her zaman pro erişime sahip
|
|
10
|
-
const isAdmin = session?.user?.role === "admin";
|
|
11
|
-
|
|
12
|
-
// Pro abonelik kontrolü
|
|
13
|
-
const hasProAccess = isAdmin || session?.user?.subscription?.status === "active";
|
|
14
|
-
const subscriptionPlan = session?.user?.subscription?.plan || (isAdmin ? "lifetime" : "free");
|
|
15
|
-
|
|
16
|
-
// Debug bilgisi
|
|
17
|
-
if (process.env.NODE_ENV === 'development') {
|
|
18
|
-
console.log('🔍 useSubscription Debug:', {
|
|
19
|
-
email: session?.user?.email,
|
|
20
|
-
role: session?.user?.role,
|
|
21
|
-
isAdmin,
|
|
22
|
-
subscription: session?.user?.subscription,
|
|
23
|
-
hasProAccess,
|
|
24
|
-
subscriptionPlan,
|
|
25
|
-
status
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
isLoading,
|
|
31
|
-
isAuthenticated,
|
|
32
|
-
isAdmin,
|
|
33
|
-
hasProAccess,
|
|
34
|
-
subscriptionPlan,
|
|
35
|
-
subscription: session?.user?.subscription,
|
|
36
|
-
};
|
|
37
|
-
}
|
package/src/use-toast.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// Toast hook - şimdilik basit bir implementasyon
|
|
2
|
-
// Gerçek implementasyonda Toaster component'i ile entegre olacak
|
|
3
|
-
|
|
4
|
-
interface ToastOptions {
|
|
5
|
-
title: string;
|
|
6
|
-
description?: string;
|
|
7
|
-
variant?: 'default' | 'destructive';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function toast(options: ToastOptions) {
|
|
11
|
-
// Şimdilik console'a yazdıralım
|
|
12
|
-
// Gerçek implementasyonda toast notification gösterilecek
|
|
13
|
-
console.log('Toast:', options);
|
|
14
|
-
|
|
15
|
-
// Browser'da alert gösterelim (geçici çözüm)
|
|
16
|
-
if (typeof window !== 'undefined') {
|
|
17
|
-
const message = options.description
|
|
18
|
-
? `${options.title}\n\n${options.description}`
|
|
19
|
-
: options.title;
|
|
20
|
-
|
|
21
|
-
// Variant'a göre stil belirle
|
|
22
|
-
if (options.variant === 'destructive') {
|
|
23
|
-
console.error(message);
|
|
24
|
-
} else {
|
|
25
|
-
console.log(message);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const useToast = () => {
|
|
31
|
-
return { toast };
|
|
32
|
-
};
|
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
import { ChartDataPoint, ChartSeries } from '../components/advanced-chart'
|
|
2
|
-
|
|
3
|
-
interface ChartTheme {
|
|
4
|
-
colors: string[]
|
|
5
|
-
backgroundColor: string
|
|
6
|
-
textColor: string
|
|
7
|
-
gridColor: string
|
|
8
|
-
tooltipBackground: string
|
|
9
|
-
tooltipBorder: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const CHART_THEMES: Record<string, ChartTheme> = {
|
|
13
|
-
default: {
|
|
14
|
-
colors: ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#06b6d4', '#f97316', '#84cc16', '#ec4899', '#6366f1'],
|
|
15
|
-
backgroundColor: '#ffffff',
|
|
16
|
-
textColor: '#374151',
|
|
17
|
-
gridColor: '#e5e7eb',
|
|
18
|
-
tooltipBackground: '#1f2937',
|
|
19
|
-
tooltipBorder: '#374151',
|
|
20
|
-
},
|
|
21
|
-
dark: {
|
|
22
|
-
colors: ['#60a5fa', '#f87171', '#34d399', '#fbbf24', '#a78bfa', '#22d3ee', '#fb923c', '#a3e635', '#f472b6', '#818cf8'],
|
|
23
|
-
backgroundColor: '#1f2937',
|
|
24
|
-
textColor: '#f9fafb',
|
|
25
|
-
gridColor: '#374151',
|
|
26
|
-
tooltipBackground: '#374151',
|
|
27
|
-
tooltipBorder: '#4b5563',
|
|
28
|
-
},
|
|
29
|
-
minimal: {
|
|
30
|
-
colors: ['#000000', '#666666', '#999999', '#cccccc'],
|
|
31
|
-
backgroundColor: '#ffffff',
|
|
32
|
-
textColor: '#000000',
|
|
33
|
-
gridColor: '#f3f4f6',
|
|
34
|
-
tooltipBackground: '#000000',
|
|
35
|
-
tooltipBorder: '#000000',
|
|
36
|
-
},
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function generateChartData(
|
|
40
|
-
count: number,
|
|
41
|
-
series: string[],
|
|
42
|
-
options?: {
|
|
43
|
-
startDate?: Date
|
|
44
|
-
interval?: 'hour' | 'day' | 'week' | 'month'
|
|
45
|
-
trend?: 'up' | 'down' | 'random'
|
|
46
|
-
baseValue?: number
|
|
47
|
-
variance?: number
|
|
48
|
-
}
|
|
49
|
-
): ChartDataPoint[] {
|
|
50
|
-
const {
|
|
51
|
-
startDate = new Date(),
|
|
52
|
-
interval = 'day',
|
|
53
|
-
trend = 'random',
|
|
54
|
-
baseValue = 100,
|
|
55
|
-
variance = 20,
|
|
56
|
-
} = options || {}
|
|
57
|
-
|
|
58
|
-
const data: ChartDataPoint[] = []
|
|
59
|
-
const current = new Date(startDate)
|
|
60
|
-
|
|
61
|
-
for (let i = 0; i < count; i++) {
|
|
62
|
-
const point: ChartDataPoint = {
|
|
63
|
-
name: formatDateForInterval(current, interval),
|
|
64
|
-
timestamp: current.getTime(),
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
series.forEach((seriesName, index) => {
|
|
68
|
-
let value = baseValue
|
|
69
|
-
|
|
70
|
-
if (trend === 'up') {
|
|
71
|
-
value += (i * 5) + (Math.random() - 0.5) * variance
|
|
72
|
-
} else if (trend === 'down') {
|
|
73
|
-
value -= (i * 5) + (Math.random() - 0.5) * variance
|
|
74
|
-
} else {
|
|
75
|
-
value += (Math.random() - 0.5) * variance * 2
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Add some series-specific variation
|
|
79
|
-
value += index * 10 + (Math.random() - 0.5) * 10
|
|
80
|
-
|
|
81
|
-
point[seriesName] = Math.max(0, Math.round(value))
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
data.push(point)
|
|
85
|
-
|
|
86
|
-
// Increment date based on interval
|
|
87
|
-
switch (interval) {
|
|
88
|
-
case 'hour':
|
|
89
|
-
current.setHours(current.getHours() + 1)
|
|
90
|
-
break
|
|
91
|
-
case 'day':
|
|
92
|
-
current.setDate(current.getDate() + 1)
|
|
93
|
-
break
|
|
94
|
-
case 'week':
|
|
95
|
-
current.setDate(current.getDate() + 7)
|
|
96
|
-
break
|
|
97
|
-
case 'month':
|
|
98
|
-
current.setMonth(current.getMonth() + 1)
|
|
99
|
-
break
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return data
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function formatDateForInterval(date: Date, interval: 'hour' | 'day' | 'week' | 'month'): string {
|
|
107
|
-
switch (interval) {
|
|
108
|
-
case 'hour':
|
|
109
|
-
return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
|
|
110
|
-
case 'day':
|
|
111
|
-
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
|
|
112
|
-
case 'week':
|
|
113
|
-
return `Week ${getWeekNumber(date)}`
|
|
114
|
-
case 'month':
|
|
115
|
-
return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })
|
|
116
|
-
default:
|
|
117
|
-
return date.toLocaleDateString()
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function getWeekNumber(date: Date): number {
|
|
122
|
-
const firstDayOfYear = new Date(date.getFullYear(), 0, 1)
|
|
123
|
-
const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000
|
|
124
|
-
return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function calculateMovingAverage(data: ChartDataPoint[], key: string, window: number): ChartDataPoint[] {
|
|
128
|
-
if (window <= 0 || window > data.length) return data
|
|
129
|
-
|
|
130
|
-
return data.map((point, index) => {
|
|
131
|
-
if (index < window - 1) return point
|
|
132
|
-
|
|
133
|
-
const windowData = data.slice(index - window + 1, index + 1)
|
|
134
|
-
const sum = windowData.reduce((acc, item) => acc + (Number(item[key]) || 0), 0)
|
|
135
|
-
const average = sum / window
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
...point,
|
|
139
|
-
[`${key}_ma${window}`]: Math.round(average * 100) / 100,
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function detectOutliers(data: ChartDataPoint[], key: string, threshold: number = 2): ChartDataPoint[] {
|
|
145
|
-
const values = data.map(point => Number(point[key]) || 0)
|
|
146
|
-
const mean = values.reduce((sum, val) => sum + val, 0) / values.length
|
|
147
|
-
const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length
|
|
148
|
-
const stdDev = Math.sqrt(variance)
|
|
149
|
-
|
|
150
|
-
return data.map(point => {
|
|
151
|
-
const value = Number(point[key]) || 0
|
|
152
|
-
const zScore = Math.abs((value - mean) / stdDev)
|
|
153
|
-
|
|
154
|
-
return {
|
|
155
|
-
...point,
|
|
156
|
-
[`${key}_outlier`]: zScore > threshold,
|
|
157
|
-
[`${key}_zscore`]: Math.round(zScore * 100) / 100,
|
|
158
|
-
} as ChartDataPoint
|
|
159
|
-
})
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function interpolateData(data: ChartDataPoint[], key: string, method: 'linear' | 'polynomial' = 'linear'): ChartDataPoint[] {
|
|
163
|
-
const result = [...data]
|
|
164
|
-
|
|
165
|
-
for (let i = 0; i < result.length; i++) {
|
|
166
|
-
const point = result[i]
|
|
167
|
-
|
|
168
|
-
if (point[key] == null || point[key] === '') {
|
|
169
|
-
// Find nearest non-null values
|
|
170
|
-
let prevIndex = i - 1
|
|
171
|
-
let nextIndex = i + 1
|
|
172
|
-
|
|
173
|
-
while (prevIndex >= 0 && (result[prevIndex][key] == null || result[prevIndex][key] === '')) {
|
|
174
|
-
prevIndex--
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
while (nextIndex < result.length && (result[nextIndex][key] == null || result[nextIndex][key] === '')) {
|
|
178
|
-
nextIndex++
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (prevIndex >= 0 && nextIndex < result.length) {
|
|
182
|
-
const prevValue = Number(result[prevIndex][key])
|
|
183
|
-
const nextValue = Number(result[nextIndex][key])
|
|
184
|
-
|
|
185
|
-
if (method === 'linear') {
|
|
186
|
-
const ratio = (i - prevIndex) / (nextIndex - prevIndex)
|
|
187
|
-
point[key] = prevValue + (nextValue - prevValue) * ratio
|
|
188
|
-
}
|
|
189
|
-
} else if (prevIndex >= 0) {
|
|
190
|
-
point[key] = result[prevIndex][key]
|
|
191
|
-
} else if (nextIndex < result.length) {
|
|
192
|
-
point[key] = result[nextIndex][key]
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return result
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function aggregateDataByPeriod(
|
|
201
|
-
data: ChartDataPoint[],
|
|
202
|
-
period: 'hour' | 'day' | 'week' | 'month',
|
|
203
|
-
aggregations: Record<string, 'sum' | 'avg' | 'min' | 'max' | 'count'>
|
|
204
|
-
): ChartDataPoint[] {
|
|
205
|
-
const groups: Record<string, ChartDataPoint[]> = {}
|
|
206
|
-
|
|
207
|
-
data.forEach(point => {
|
|
208
|
-
const timestamp = point.timestamp ? new Date(point.timestamp as number) : new Date()
|
|
209
|
-
const key = formatDateForInterval(timestamp, period)
|
|
210
|
-
|
|
211
|
-
if (!groups[key]) {
|
|
212
|
-
groups[key] = []
|
|
213
|
-
}
|
|
214
|
-
groups[key].push(point)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
return Object.entries(groups).map(([key, groupData]) => {
|
|
218
|
-
const result: ChartDataPoint = { name: key }
|
|
219
|
-
|
|
220
|
-
Object.entries(aggregations).forEach(([field, operation]) => {
|
|
221
|
-
const values = groupData.map(point => Number(point[field]) || 0)
|
|
222
|
-
|
|
223
|
-
switch (operation) {
|
|
224
|
-
case 'sum':
|
|
225
|
-
result[field] = values.reduce((sum, val) => sum + val, 0)
|
|
226
|
-
break
|
|
227
|
-
case 'avg':
|
|
228
|
-
result[field] = values.reduce((sum, val) => sum + val, 0) / values.length
|
|
229
|
-
break
|
|
230
|
-
case 'min':
|
|
231
|
-
result[field] = Math.min(...values)
|
|
232
|
-
break
|
|
233
|
-
case 'max':
|
|
234
|
-
result[field] = Math.max(...values)
|
|
235
|
-
break
|
|
236
|
-
case 'count':
|
|
237
|
-
result[field] = values.length
|
|
238
|
-
break
|
|
239
|
-
}
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
return result
|
|
243
|
-
})
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
export function exportChartAsImage(
|
|
247
|
-
chartElement: HTMLElement,
|
|
248
|
-
filename: string = 'chart',
|
|
249
|
-
format: 'png' | 'jpeg' | 'svg' = 'png'
|
|
250
|
-
): void {
|
|
251
|
-
// This would typically use html2canvas or similar library
|
|
252
|
-
// For now, we'll provide a basic implementation
|
|
253
|
-
console.log(`Exporting chart as ${format} with filename: ${filename}`)
|
|
254
|
-
|
|
255
|
-
// Implementation would go here
|
|
256
|
-
// This is a placeholder for the actual export functionality
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
export function exportChartData(
|
|
260
|
-
data: ChartDataPoint[],
|
|
261
|
-
filename: string = 'chart-data',
|
|
262
|
-
format: 'json' | 'csv' = 'json'
|
|
263
|
-
): void {
|
|
264
|
-
let content: string
|
|
265
|
-
let mimeType: string
|
|
266
|
-
|
|
267
|
-
if (format === 'json') {
|
|
268
|
-
content = JSON.stringify(data, null, 2)
|
|
269
|
-
mimeType = 'application/json'
|
|
270
|
-
} else {
|
|
271
|
-
// CSV export
|
|
272
|
-
const headers = Object.keys(data[0] || {})
|
|
273
|
-
const csvRows = [
|
|
274
|
-
headers.join(','),
|
|
275
|
-
...data.map(row =>
|
|
276
|
-
headers.map(header => {
|
|
277
|
-
const value = row[header]
|
|
278
|
-
return typeof value === 'string' && value.includes(',')
|
|
279
|
-
? `"${value}"`
|
|
280
|
-
: value
|
|
281
|
-
}).join(',')
|
|
282
|
-
)
|
|
283
|
-
]
|
|
284
|
-
content = csvRows.join('\n')
|
|
285
|
-
mimeType = 'text/csv'
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const blob = new Blob([content], { type: mimeType })
|
|
289
|
-
const url = window.URL.createObjectURL(blob)
|
|
290
|
-
const link = document.createElement('a')
|
|
291
|
-
link.href = url
|
|
292
|
-
link.download = `${filename}.${format}`
|
|
293
|
-
document.body.appendChild(link)
|
|
294
|
-
link.click()
|
|
295
|
-
document.body.removeChild(link)
|
|
296
|
-
window.URL.revokeObjectURL(url)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Gradient color generator
|
|
300
|
-
export function generateGradientColors(baseColor: string, count: number = 5): string[] {
|
|
301
|
-
const colors: string[] = []
|
|
302
|
-
const rgb = hexToRgb(baseColor)
|
|
303
|
-
|
|
304
|
-
if (!rgb) return [baseColor]
|
|
305
|
-
|
|
306
|
-
for (let i = 0; i < count; i++) {
|
|
307
|
-
const factor = i / (count - 1)
|
|
308
|
-
const r = Math.round(rgb.r + (255 - rgb.r) * factor * 0.5)
|
|
309
|
-
const g = Math.round(rgb.g + (255 - rgb.g) * factor * 0.5)
|
|
310
|
-
const b = Math.round(rgb.b + (255 - rgb.b) * factor * 0.5)
|
|
311
|
-
colors.push(`rgb(${r}, ${g}, ${b})`)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return colors
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
318
|
-
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
|
319
|
-
return result ? {
|
|
320
|
-
r: parseInt(result[1], 16),
|
|
321
|
-
g: parseInt(result[2], 16),
|
|
322
|
-
b: parseInt(result[3], 16)
|
|
323
|
-
} : null
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Performance optimization helpers
|
|
327
|
-
export function throttleChartData(data: ChartDataPoint[], maxPoints: number = 1000): ChartDataPoint[] {
|
|
328
|
-
if (data.length <= maxPoints) return data
|
|
329
|
-
|
|
330
|
-
const step = Math.ceil(data.length / maxPoints)
|
|
331
|
-
return data.filter((_, index) => index % step === 0)
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Format large numbers
|
|
335
|
-
export function formatChartValue(value: number): string {
|
|
336
|
-
if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`
|
|
337
|
-
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`
|
|
338
|
-
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`
|
|
339
|
-
return value.toString()
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Calculate chart statistics
|
|
343
|
-
export function calculateChartStats(data: ChartDataPoint[], keys: string[]): Record<string, { min: number; max: number; avg: number; total: number }> {
|
|
344
|
-
const stats: Record<string, { min: number; max: number; avg: number; total: number }> = {}
|
|
345
|
-
|
|
346
|
-
keys.forEach(key => {
|
|
347
|
-
const values = data.map(point => Number(point[key]) || 0)
|
|
348
|
-
stats[key] = {
|
|
349
|
-
min: Math.min(...values),
|
|
350
|
-
max: Math.max(...values),
|
|
351
|
-
avg: values.reduce((sum, val) => sum + val, 0) / values.length,
|
|
352
|
-
total: values.reduce((sum, val) => sum + val, 0)
|
|
353
|
-
}
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
return stats
|
|
357
|
-
}
|