@wakastellar/ui 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.d.ts +15 -0
- package/dist/components/waka-ad-banner/index.d.ts +36 -0
- package/dist/components/waka-ad-fallback/index.d.ts +33 -0
- package/dist/components/waka-ad-inline/index.d.ts +15 -0
- package/dist/components/waka-ad-interstitial/index.d.ts +26 -0
- package/dist/components/waka-ad-placeholder/index.d.ts +17 -0
- package/dist/components/waka-ad-provider/index.d.ts +103 -0
- package/dist/components/waka-ad-sidebar/index.d.ts +18 -0
- package/dist/components/waka-ad-sticky-footer/index.d.ts +17 -0
- package/dist/components/waka-content-recommendation/index.d.ts +23 -0
- package/dist/components/waka-outstream-video/index.d.ts +24 -0
- package/dist/components/waka-sponsored-badge/index.d.ts +20 -0
- package/dist/components/waka-sponsored-card/index.d.ts +25 -0
- package/dist/components/waka-sponsored-feed/index.d.ts +31 -0
- package/dist/components/waka-video-ad/index.d.ts +32 -0
- package/dist/components/waka-video-overlay/index.d.ts +26 -0
- package/dist/index.cjs.js +177 -171
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +14535 -12812
- package/dist/utils/security.d.ts +96 -0
- package/package.json +4 -4
- package/src/blocks/sidebar/index.tsx +6 -6
- package/src/components/DataTable/templates/index.tsx +3 -2
- package/src/components/index.ts +94 -0
- package/src/components/waka-3d-pie-chart/index.tsx +11 -11
- package/src/components/waka-achievement-unlock/index.tsx +16 -16
- package/src/components/waka-ad-banner/index.tsx +275 -0
- package/src/components/waka-ad-fallback/index.tsx +181 -0
- package/src/components/waka-ad-inline/index.tsx +103 -0
- package/src/components/waka-ad-interstitial/index.tsx +278 -0
- package/src/components/waka-ad-placeholder/index.tsx +84 -0
- package/src/components/waka-ad-provider/index.tsx +329 -0
- package/src/components/waka-ad-sidebar/index.tsx +113 -0
- package/src/components/waka-ad-sticky-footer/index.tsx +125 -0
- package/src/components/waka-badge-showcase/index.tsx +12 -11
- package/src/components/waka-command-bar/index.tsx +2 -1
- package/src/components/waka-content-recommendation/index.tsx +294 -0
- package/src/components/waka-cost-breakdown/index.tsx +10 -10
- package/src/components/waka-funnel-chart/index.tsx +8 -8
- package/src/components/waka-health-pulse/index.tsx +6 -6
- package/src/components/waka-leaderboard/index.tsx +9 -9
- package/src/components/waka-loot-box/index.tsx +20 -20
- package/src/components/waka-outstream-video/index.tsx +240 -0
- package/src/components/waka-player-card/index.tsx +5 -5
- package/src/components/waka-quota-bar/index.tsx +4 -4
- package/src/components/waka-radar-score/index.tsx +10 -10
- package/src/components/waka-scratch-card/index.tsx +5 -4
- package/src/components/waka-server-rack/index.tsx +28 -27
- package/src/components/waka-sponsored-badge/index.tsx +97 -0
- package/src/components/waka-sponsored-card/index.tsx +275 -0
- package/src/components/waka-sponsored-feed/index.tsx +127 -0
- package/src/components/waka-spotlight/index.tsx +2 -1
- package/src/components/waka-success-explosion/index.tsx +4 -4
- package/src/components/waka-video-ad/index.tsx +406 -0
- package/src/components/waka-video-overlay/index.tsx +257 -0
- package/src/components/waka-xp-bar/index.tsx +13 -13
- package/src/styles/base.css +16 -0
- package/src/styles/tailwind.preset.js +12 -0
- package/src/styles/themes/forest.css +16 -0
- package/src/styles/themes/monochrome.css +16 -0
- package/src/styles/themes/perpetuity.css +16 -0
- package/src/styles/themes/sunset.css +16 -0
- package/src/styles/themes/twilight.css +16 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { useRef, useEffect, useState } from "react"
|
|
5
|
+
import { cn } from "../../utils/cn"
|
|
6
|
+
import { useAdContext, useAdVisibility, type CustomAd } from "../waka-ad-provider"
|
|
7
|
+
import { WakaSponsoredBadge } from "../waka-sponsored-badge"
|
|
8
|
+
import { ExternalLink, ChevronLeft, ChevronRight } from "lucide-react"
|
|
9
|
+
|
|
10
|
+
export type RecommendationLayout = "grid" | "carousel" | "list"
|
|
11
|
+
|
|
12
|
+
export interface WakaContentRecommendationProps {
|
|
13
|
+
/** Widget title */
|
|
14
|
+
title?: string
|
|
15
|
+
/** Slot IDs for recommendations */
|
|
16
|
+
slotIds: string[]
|
|
17
|
+
/** Layout style */
|
|
18
|
+
layout?: RecommendationLayout
|
|
19
|
+
/** Number of columns for grid layout */
|
|
20
|
+
columns?: 2 | 3 | 4
|
|
21
|
+
/** Show navigation arrows for carousel */
|
|
22
|
+
showArrows?: boolean
|
|
23
|
+
/** Auto-scroll carousel */
|
|
24
|
+
autoScroll?: boolean
|
|
25
|
+
/** Auto-scroll interval (seconds) */
|
|
26
|
+
autoScrollInterval?: number
|
|
27
|
+
/** Custom class name */
|
|
28
|
+
className?: string
|
|
29
|
+
/** Callback when item is clicked */
|
|
30
|
+
onItemClick?: (slotId: string) => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function WakaContentRecommendation({
|
|
34
|
+
title = "You May Also Like",
|
|
35
|
+
slotIds,
|
|
36
|
+
layout = "grid",
|
|
37
|
+
columns = 3,
|
|
38
|
+
showArrows = true,
|
|
39
|
+
autoScroll = false,
|
|
40
|
+
autoScrollInterval = 5,
|
|
41
|
+
className,
|
|
42
|
+
onItemClick,
|
|
43
|
+
}: WakaContentRecommendationProps) {
|
|
44
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
45
|
+
const carouselRef = useRef<HTMLDivElement>(null)
|
|
46
|
+
const { config, isReady, hasConsent, getCustomAd, trackEvent } = useAdContext()
|
|
47
|
+
const { isVisible } = useAdVisibility(containerRef)
|
|
48
|
+
|
|
49
|
+
const [ads, setAds] = useState<Map<string, CustomAd>>(new Map())
|
|
50
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
51
|
+
const [currentSlide, setCurrentSlide] = useState(0)
|
|
52
|
+
|
|
53
|
+
// Load all ads
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!isReady || !isVisible || hasConsent === false) return
|
|
56
|
+
|
|
57
|
+
const loadAds = async () => {
|
|
58
|
+
setIsLoading(true)
|
|
59
|
+
const loadedAds = new Map<string, CustomAd>()
|
|
60
|
+
|
|
61
|
+
await Promise.all(
|
|
62
|
+
slotIds.map(async (slotId) => {
|
|
63
|
+
try {
|
|
64
|
+
const ad = await getCustomAd(slotId)
|
|
65
|
+
if (ad) {
|
|
66
|
+
loadedAds.set(slotId, ad)
|
|
67
|
+
trackEvent({ type: "loaded", slotId, timestamp: new Date() })
|
|
68
|
+
|
|
69
|
+
// Fire impression tracking
|
|
70
|
+
if (ad.impressionUrl) {
|
|
71
|
+
fetch(ad.impressionUrl, { mode: "no-cors" }).catch(() => {})
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Ignore errors for individual slots
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
setAds(loadedAds)
|
|
81
|
+
setIsLoading(false)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
loadAds()
|
|
85
|
+
}, [isReady, isVisible, hasConsent, slotIds, getCustomAd, trackEvent])
|
|
86
|
+
|
|
87
|
+
// Track viewability for loaded ads
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (isVisible && ads.size > 0) {
|
|
90
|
+
ads.forEach((_, slotId) => {
|
|
91
|
+
trackEvent({ type: "viewable", slotId, timestamp: new Date() })
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
}, [isVisible, ads, trackEvent])
|
|
95
|
+
|
|
96
|
+
// Auto-scroll for carousel
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (layout !== "carousel" || !autoScroll || ads.size === 0) return
|
|
99
|
+
|
|
100
|
+
const interval = setInterval(() => {
|
|
101
|
+
setCurrentSlide((prev) => (prev + 1) % ads.size)
|
|
102
|
+
}, autoScrollInterval * 1000)
|
|
103
|
+
|
|
104
|
+
return () => clearInterval(interval)
|
|
105
|
+
}, [layout, autoScroll, autoScrollInterval, ads.size])
|
|
106
|
+
|
|
107
|
+
// Scroll carousel to current slide
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
if (layout !== "carousel" || !carouselRef.current) return
|
|
110
|
+
|
|
111
|
+
const slideWidth = carouselRef.current.offsetWidth / Math.min(columns, ads.size)
|
|
112
|
+
carouselRef.current.scrollTo({
|
|
113
|
+
left: currentSlide * slideWidth,
|
|
114
|
+
behavior: "smooth",
|
|
115
|
+
})
|
|
116
|
+
}, [currentSlide, layout, columns, ads.size])
|
|
117
|
+
|
|
118
|
+
const handleItemClick = (slotId: string, ad: CustomAd) => {
|
|
119
|
+
trackEvent({ type: "click", slotId, timestamp: new Date() })
|
|
120
|
+
onItemClick?.(slotId)
|
|
121
|
+
|
|
122
|
+
if (ad.clickUrl) {
|
|
123
|
+
fetch(ad.clickUrl, { mode: "no-cors" }).catch(() => {})
|
|
124
|
+
}
|
|
125
|
+
if (ad.targetUrl) {
|
|
126
|
+
window.open(ad.targetUrl, "_blank", "noopener,noreferrer")
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const handlePrev = () => {
|
|
131
|
+
setCurrentSlide((prev) => (prev - 1 + ads.size) % ads.size)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const handleNext = () => {
|
|
135
|
+
setCurrentSlide((prev) => (prev + 1) % ads.size)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (ads.size === 0 && !isLoading) {
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const columnClasses = {
|
|
143
|
+
2: "grid-cols-1 sm:grid-cols-2",
|
|
144
|
+
3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
145
|
+
4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const renderItem = (slotId: string, ad: CustomAd) => (
|
|
149
|
+
<button
|
|
150
|
+
key={slotId}
|
|
151
|
+
onClick={() => handleItemClick(slotId, ad)}
|
|
152
|
+
className="group flex-shrink-0 w-full text-left rounded-lg overflow-hidden bg-card border hover:shadow-md transition-shadow"
|
|
153
|
+
>
|
|
154
|
+
{ad.imageUrl && (
|
|
155
|
+
<div className="relative aspect-video bg-muted overflow-hidden">
|
|
156
|
+
<img
|
|
157
|
+
src={ad.imageUrl}
|
|
158
|
+
alt=""
|
|
159
|
+
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
|
160
|
+
/>
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
|
|
164
|
+
<div className="p-3">
|
|
165
|
+
<h4 className="font-medium text-sm line-clamp-2 mb-1 group-hover:text-primary transition-colors">
|
|
166
|
+
{ad.title}
|
|
167
|
+
</h4>
|
|
168
|
+
|
|
169
|
+
<div className="flex items-center justify-between">
|
|
170
|
+
<span className="text-xs text-muted-foreground">
|
|
171
|
+
{ad.sponsor || "Sponsored"}
|
|
172
|
+
</span>
|
|
173
|
+
<ExternalLink className="h-3 w-3 text-muted-foreground" />
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</button>
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<div
|
|
181
|
+
ref={containerRef}
|
|
182
|
+
className={cn("relative", className)}
|
|
183
|
+
>
|
|
184
|
+
{/* Header */}
|
|
185
|
+
<div className="flex items-center justify-between mb-4">
|
|
186
|
+
<div className="flex items-center gap-3">
|
|
187
|
+
<h3 className="font-semibold text-lg">{title}</h3>
|
|
188
|
+
<WakaSponsoredBadge size="sm" showIcon={false} />
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
{layout === "carousel" && showArrows && ads.size > columns && (
|
|
192
|
+
<div className="flex items-center gap-2">
|
|
193
|
+
<button
|
|
194
|
+
onClick={handlePrev}
|
|
195
|
+
className="p-1.5 rounded-full bg-muted hover:bg-muted-foreground/20 transition-colors"
|
|
196
|
+
aria-label="Previous"
|
|
197
|
+
>
|
|
198
|
+
<ChevronLeft className="h-4 w-4" />
|
|
199
|
+
</button>
|
|
200
|
+
<button
|
|
201
|
+
onClick={handleNext}
|
|
202
|
+
className="p-1.5 rounded-full bg-muted hover:bg-muted-foreground/20 transition-colors"
|
|
203
|
+
aria-label="Next"
|
|
204
|
+
>
|
|
205
|
+
<ChevronRight className="h-4 w-4" />
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
{/* Content */}
|
|
212
|
+
{isLoading ? (
|
|
213
|
+
<div className={cn("grid gap-4", columnClasses[columns])}>
|
|
214
|
+
{slotIds.slice(0, columns).map((_, i) => (
|
|
215
|
+
<div
|
|
216
|
+
key={i}
|
|
217
|
+
className="rounded-lg border bg-muted animate-pulse aspect-[4/3]"
|
|
218
|
+
/>
|
|
219
|
+
))}
|
|
220
|
+
</div>
|
|
221
|
+
) : layout === "grid" ? (
|
|
222
|
+
<div className={cn("grid gap-4", columnClasses[columns])}>
|
|
223
|
+
{Array.from(ads.entries()).map(([slotId, ad]) => renderItem(slotId, ad))}
|
|
224
|
+
</div>
|
|
225
|
+
) : layout === "carousel" ? (
|
|
226
|
+
<div
|
|
227
|
+
ref={carouselRef}
|
|
228
|
+
className="flex gap-4 overflow-x-auto scrollbar-hide snap-x snap-mandatory"
|
|
229
|
+
style={{ scrollbarWidth: "none", msOverflowStyle: "none" }}
|
|
230
|
+
>
|
|
231
|
+
{Array.from(ads.entries()).map(([slotId, ad]) => (
|
|
232
|
+
<div
|
|
233
|
+
key={slotId}
|
|
234
|
+
className="snap-start"
|
|
235
|
+
style={{ minWidth: `calc((100% - ${(columns - 1) * 16}px) / ${columns})` }}
|
|
236
|
+
>
|
|
237
|
+
{renderItem(slotId, ad)}
|
|
238
|
+
</div>
|
|
239
|
+
))}
|
|
240
|
+
</div>
|
|
241
|
+
) : (
|
|
242
|
+
<div className="space-y-3">
|
|
243
|
+
{Array.from(ads.entries()).map(([slotId, ad]) => (
|
|
244
|
+
<button
|
|
245
|
+
key={slotId}
|
|
246
|
+
onClick={() => handleItemClick(slotId, ad)}
|
|
247
|
+
className="group w-full flex gap-3 p-2 rounded-lg border hover:bg-accent/50 transition-colors text-left"
|
|
248
|
+
>
|
|
249
|
+
{ad.imageUrl && (
|
|
250
|
+
<div className="w-20 h-20 flex-shrink-0 rounded-md overflow-hidden bg-muted">
|
|
251
|
+
<img
|
|
252
|
+
src={ad.imageUrl}
|
|
253
|
+
alt=""
|
|
254
|
+
className="w-full h-full object-cover"
|
|
255
|
+
/>
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
|
|
259
|
+
<div className="flex-1 min-w-0">
|
|
260
|
+
<h4 className="font-medium text-sm line-clamp-2 mb-1 group-hover:text-primary transition-colors">
|
|
261
|
+
{ad.title}
|
|
262
|
+
</h4>
|
|
263
|
+
<span className="text-xs text-muted-foreground">
|
|
264
|
+
{ad.sponsor || "Sponsored"}
|
|
265
|
+
</span>
|
|
266
|
+
</div>
|
|
267
|
+
</button>
|
|
268
|
+
))}
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
|
|
272
|
+
{/* Pagination dots for carousel */}
|
|
273
|
+
{layout === "carousel" && ads.size > columns && (
|
|
274
|
+
<div className="flex justify-center gap-1.5 mt-4">
|
|
275
|
+
{Array.from({ length: Math.ceil(ads.size / columns) }).map((_, i) => (
|
|
276
|
+
<button
|
|
277
|
+
key={i}
|
|
278
|
+
onClick={() => setCurrentSlide(i * columns)}
|
|
279
|
+
className={cn(
|
|
280
|
+
"w-2 h-2 rounded-full transition-colors",
|
|
281
|
+
Math.floor(currentSlide / columns) === i
|
|
282
|
+
? "bg-primary"
|
|
283
|
+
: "bg-muted-foreground/30"
|
|
284
|
+
)}
|
|
285
|
+
aria-label={`Go to slide ${i + 1}`}
|
|
286
|
+
/>
|
|
287
|
+
))}
|
|
288
|
+
</div>
|
|
289
|
+
)}
|
|
290
|
+
</div>
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export default WakaContentRecommendation
|
|
@@ -48,16 +48,16 @@ export interface WakaCostBreakdownProps {
|
|
|
48
48
|
// ============================================================================
|
|
49
49
|
|
|
50
50
|
const defaultColors = [
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
51
|
+
"hsl(var(--chart-1))",
|
|
52
|
+
"hsl(var(--chart-2))",
|
|
53
|
+
"hsl(var(--chart-3))",
|
|
54
|
+
"hsl(var(--chart-4))",
|
|
55
|
+
"hsl(var(--chart-5))",
|
|
56
|
+
"hsl(var(--primary))",
|
|
57
|
+
"hsl(var(--info))",
|
|
58
|
+
"hsl(var(--warning))",
|
|
59
|
+
"hsl(var(--success))",
|
|
60
|
+
"hsl(var(--destructive))",
|
|
61
61
|
]
|
|
62
62
|
|
|
63
63
|
// ============================================================================
|
|
@@ -193,14 +193,14 @@ function formatValue(
|
|
|
193
193
|
// ============================================================================
|
|
194
194
|
|
|
195
195
|
const defaultColors = [
|
|
196
|
-
"
|
|
197
|
-
"
|
|
198
|
-
"
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"
|
|
196
|
+
"hsl(var(--chart-1))",
|
|
197
|
+
"hsl(var(--chart-2))",
|
|
198
|
+
"hsl(var(--chart-3))",
|
|
199
|
+
"hsl(var(--chart-4))",
|
|
200
|
+
"hsl(var(--chart-5))",
|
|
201
|
+
"hsl(var(--primary))",
|
|
202
|
+
"hsl(var(--info))",
|
|
203
|
+
"hsl(var(--destructive))",
|
|
204
204
|
]
|
|
205
205
|
|
|
206
206
|
function getStageColor(index: number, stage: FunnelStage): string {
|
|
@@ -33,15 +33,15 @@ export interface WakaHealthPulseProps {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// ============================================
|
|
36
|
-
// DEFAULT COLORS
|
|
36
|
+
// DEFAULT COLORS (using CSS variables for theme support)
|
|
37
37
|
// ============================================
|
|
38
38
|
|
|
39
39
|
const defaultColors: Record<HealthStatus, string> = {
|
|
40
|
-
healthy: "
|
|
41
|
-
warning: "
|
|
42
|
-
critical: "
|
|
43
|
-
down: "
|
|
44
|
-
unknown: "
|
|
40
|
+
healthy: "hsl(var(--success))",
|
|
41
|
+
warning: "hsl(var(--warning))",
|
|
42
|
+
critical: "hsl(var(--destructive))",
|
|
43
|
+
down: "hsl(var(--muted-foreground))",
|
|
44
|
+
unknown: "hsl(var(--chart-3))",
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// ============================================
|
|
@@ -77,30 +77,30 @@ const medalConfig = {
|
|
|
77
77
|
1: {
|
|
78
78
|
gradient: "from-yellow-300 via-yellow-400 to-yellow-500",
|
|
79
79
|
shadow: "shadow-yellow-400/50",
|
|
80
|
-
glow: "
|
|
80
|
+
glow: "hsl(var(--warning))",
|
|
81
81
|
border: "border-yellow-400",
|
|
82
|
-
bg: "bg-yellow-50",
|
|
83
|
-
text: "text-yellow-700",
|
|
82
|
+
bg: "bg-yellow-50 dark:bg-yellow-950/30",
|
|
83
|
+
text: "text-yellow-700 dark:text-yellow-400",
|
|
84
84
|
icon: Crown,
|
|
85
85
|
label: "Gold",
|
|
86
86
|
},
|
|
87
87
|
2: {
|
|
88
88
|
gradient: "from-gray-300 via-gray-400 to-gray-500",
|
|
89
89
|
shadow: "shadow-gray-400/50",
|
|
90
|
-
glow: "
|
|
90
|
+
glow: "hsl(var(--muted-foreground))",
|
|
91
91
|
border: "border-gray-400",
|
|
92
|
-
bg: "bg-gray-50",
|
|
93
|
-
text: "text-gray-700",
|
|
92
|
+
bg: "bg-gray-50 dark:bg-gray-950/30",
|
|
93
|
+
text: "text-gray-700 dark:text-gray-400",
|
|
94
94
|
icon: Medal,
|
|
95
95
|
label: "Silver",
|
|
96
96
|
},
|
|
97
97
|
3: {
|
|
98
98
|
gradient: "from-amber-500 via-amber-600 to-amber-700",
|
|
99
99
|
shadow: "shadow-amber-500/50",
|
|
100
|
-
glow: "
|
|
100
|
+
glow: "hsl(var(--chart-4))",
|
|
101
101
|
border: "border-amber-500",
|
|
102
|
-
bg: "bg-amber-50",
|
|
103
|
-
text: "text-amber-700",
|
|
102
|
+
bg: "bg-amber-50 dark:bg-amber-950/30",
|
|
103
|
+
text: "text-amber-700 dark:text-amber-400",
|
|
104
104
|
icon: Medal,
|
|
105
105
|
label: "Bronze",
|
|
106
106
|
},
|
|
@@ -102,46 +102,46 @@ const rarityConfig = {
|
|
|
102
102
|
common: {
|
|
103
103
|
gradient: "from-slate-400 to-slate-600",
|
|
104
104
|
glow: "shadow-slate-400/50",
|
|
105
|
-
glowColor: "
|
|
105
|
+
glowColor: "hsl(var(--muted-foreground))",
|
|
106
106
|
borderColor: "border-slate-400",
|
|
107
|
-
bgColor: "bg-slate-100",
|
|
108
|
-
textColor: "text-slate-600",
|
|
109
|
-
particleColors: ["
|
|
107
|
+
bgColor: "bg-slate-100 dark:bg-slate-900",
|
|
108
|
+
textColor: "text-slate-600 dark:text-slate-400",
|
|
109
|
+
particleColors: ["hsl(var(--muted-foreground))", "hsl(var(--muted))", "hsl(var(--border))"],
|
|
110
110
|
label: "Common",
|
|
111
|
-
beamColor: "
|
|
111
|
+
beamColor: "hsl(var(--muted-foreground) / 0.8)",
|
|
112
112
|
},
|
|
113
113
|
rare: {
|
|
114
114
|
gradient: "from-blue-400 to-blue-600",
|
|
115
115
|
glow: "shadow-blue-400/50",
|
|
116
|
-
glowColor: "
|
|
116
|
+
glowColor: "hsl(var(--info))",
|
|
117
117
|
borderColor: "border-blue-400",
|
|
118
|
-
bgColor: "bg-blue-100",
|
|
119
|
-
textColor: "text-blue-600",
|
|
120
|
-
particleColors: ["
|
|
118
|
+
bgColor: "bg-blue-100 dark:bg-blue-950",
|
|
119
|
+
textColor: "text-blue-600 dark:text-blue-400",
|
|
120
|
+
particleColors: ["hsl(var(--info))", "hsl(var(--chart-1))", "hsl(var(--primary))"],
|
|
121
121
|
label: "Rare",
|
|
122
|
-
beamColor: "
|
|
122
|
+
beamColor: "hsl(var(--info) / 0.8)",
|
|
123
123
|
},
|
|
124
124
|
epic: {
|
|
125
125
|
gradient: "from-purple-400 to-purple-600",
|
|
126
126
|
glow: "shadow-purple-400/50",
|
|
127
|
-
glowColor: "
|
|
127
|
+
glowColor: "hsl(var(--chart-2))",
|
|
128
128
|
borderColor: "border-purple-400",
|
|
129
|
-
bgColor: "bg-purple-100",
|
|
130
|
-
textColor: "text-purple-600",
|
|
131
|
-
particleColors: ["
|
|
129
|
+
bgColor: "bg-purple-100 dark:bg-purple-950",
|
|
130
|
+
textColor: "text-purple-600 dark:text-purple-400",
|
|
131
|
+
particleColors: ["hsl(var(--chart-2))", "hsl(var(--chart-3))", "hsl(var(--primary))"],
|
|
132
132
|
label: "Epic",
|
|
133
|
-
beamColor: "
|
|
133
|
+
beamColor: "hsl(var(--chart-2) / 0.8)",
|
|
134
134
|
},
|
|
135
135
|
legendary: {
|
|
136
136
|
gradient: "from-amber-400 via-orange-500 to-red-500",
|
|
137
137
|
glow: "shadow-amber-400/50",
|
|
138
|
-
glowColor: "
|
|
138
|
+
glowColor: "hsl(var(--warning))",
|
|
139
139
|
borderColor: "border-amber-400",
|
|
140
|
-
bgColor: "bg-amber-100",
|
|
141
|
-
textColor: "text-amber-600",
|
|
142
|
-
particleColors: ["
|
|
140
|
+
bgColor: "bg-amber-100 dark:bg-amber-950",
|
|
141
|
+
textColor: "text-amber-600 dark:text-amber-400",
|
|
142
|
+
particleColors: ["hsl(var(--warning))", "hsl(var(--chart-4))", "hsl(var(--destructive))", "hsl(var(--chart-5))"],
|
|
143
143
|
label: "Legendary",
|
|
144
|
-
beamColor: "
|
|
144
|
+
beamColor: "hsl(var(--warning) / 0.9)",
|
|
145
145
|
},
|
|
146
146
|
}
|
|
147
147
|
|