Skip to content

Commit bf82298

Browse files
committed
craft: loading
fix Update preview-dialog.tsx
1 parent d67dc03 commit bf82298

8 files changed

Lines changed: 152 additions & 47 deletions

File tree

apps/web/app/publish/demo/page.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,7 @@ function AddDemoContent() {
107107
}
108108

109109
if (isLoading || !componentData) {
110-
return (
111-
<LoadingSpinnerPage
112-
text="Loading component..."
113-
size="lg"
114-
showText={true}
115-
/>
116-
)
110+
return <LoadingSpinnerPage size="lg" />
117111
}
118112

119113
return (
@@ -130,11 +124,7 @@ function AddDemoContent() {
130124

131125
export default function AddDemoPage() {
132126
return (
133-
<Suspense
134-
fallback={
135-
<LoadingSpinnerPage text="Loading..." size="lg" showText={true} />
136-
}
137-
>
127+
<Suspense fallback={<LoadingSpinnerPage size="lg" />}>
138128
<AddDemoContent />
139129
</Suspense>
140130
)

apps/web/components/features/component-page/component-preview.tsx

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -231,21 +231,6 @@ export function ComponentPagePreview({
231231
)
232232

233233
const [isLoading, setIsLoading] = useState(true)
234-
const [loadingText, setLoadingText] = useState("Starting preview...")
235-
236-
useEffect(() => {
237-
if (isLoading) {
238-
const timer = setTimeout(() => {
239-
setLoadingText(
240-
"Loading is taking longer than usual... you may want to refresh the page",
241-
)
242-
}, 10000)
243-
244-
return () => {
245-
clearTimeout(timer)
246-
}
247-
}
248-
}, [isLoading])
249234

250235
if (!css)
251236
return (
@@ -336,7 +321,7 @@ export function ComponentPagePreview({
336321

337322
{isLoading && (
338323
<div className="flex flex-col items-center justify-center h-full gap-3">
339-
<LoadingSpinner text={loadingText} />
324+
<LoadingSpinner />
340325
</div>
341326
)}
342327
{(demo.bundle_html_url ||

apps/web/components/features/component-page/preview-dialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
DropdownMenuRadioItem,
1919
DropdownMenuTrigger,
2020
} from "@/components/ui/dropdown-menu"
21+
import { LoadingSpinner } from "@/components/ui/loading-spinner"
2122
import {
2223
Tooltip,
2324
TooltipContent,
@@ -71,7 +72,7 @@ const selectedPromptTypeAtom = atomWithStorage<PromptType | "v0-open">(
7172
export function PreviewSkeleton() {
7273
return (
7374
<div className="flex-1 animate-pulse bg-muted flex items-center justify-center">
74-
<Spinner />
75+
<LoadingSpinner />
7576
</div>
7677
)
7778
}

apps/web/components/ui/loading-dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function LoadingDialog({ isOpen, message }: LoadingDialogProps) {
1313
hideCloseButton
1414
className="w-[425px] h-40 flex flex-col items-center justify-center gap-4"
1515
>
16-
<LoadingSpinner showText={false} />
16+
<LoadingSpinner />
1717
<p className="text-center text-sm text-muted-foreground">{message}</p>
1818
</DialogContent>
1919
</Dialog>
Lines changed: 126 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,162 @@
1+
"use client"
2+
13
import { cn } from "@/lib/utils"
4+
import Lottie from "lottie-react"
5+
import { useEffect, useState, useRef } from "react"
6+
7+
// Animation cache to prevent multiple fetches of the same file
8+
const animationCache = new Map<string, any>()
9+
10+
// Preload the default animation
11+
if (typeof window !== "undefined") {
12+
fetch("/loading.json")
13+
.then((response) => response.json())
14+
.then((data) => {
15+
animationCache.set("/loading.json", data)
16+
})
17+
.catch((error) => console.error("Error preloading animation:", error))
18+
}
219

320
interface LoadingSpinnerProps {
4-
text?: string
521
size?: "sm" | "md" | "lg"
6-
showText?: boolean
722
className?: string
23+
animationFile?: string
24+
logoFill?: string
825
}
926

1027
export const LoadingSpinner = ({
11-
text = "Loading...",
12-
size = "md",
13-
showText = true,
28+
size = "sm",
1429
className,
30+
animationFile = "/loading.json",
31+
logoFill = "currentColor",
1532
}: LoadingSpinnerProps) => {
16-
const sizeClasses = {
17-
sm: "w-3 h-3",
18-
md: "w-4 h-4",
19-
lg: "w-6 h-6",
33+
const [animationData, setAnimationData] = useState<any>(
34+
animationCache.get(animationFile) || null,
35+
)
36+
const [lottieReady, setLottieReady] = useState(false)
37+
const hasFetchedRef = useRef(false)
38+
39+
// Унифицированные размеры для SVG и Lottie
40+
const sizeMap = {
41+
sm: { className: "w-8 h-8", style: { width: "2rem", height: "2rem" } },
42+
md: { className: "w-12 h-12", style: { width: "3rem", height: "3rem" } },
43+
lg: { className: "w-16 h-16", style: { width: "4rem", height: "4rem" } },
2044
}
2145

46+
useEffect(() => {
47+
// Skip fetch if we already have the animation in cache
48+
if (animationCache.has(animationFile)) {
49+
setAnimationData(animationCache.get(animationFile))
50+
return
51+
}
52+
53+
// Skip duplicate fetches
54+
if (hasFetchedRef.current) return
55+
hasFetchedRef.current = true
56+
57+
// We'll use the JSON file as default
58+
if (animationFile && animationFile.endsWith(".json")) {
59+
fetch(animationFile)
60+
.then((response) => response.json())
61+
.then((data) => {
62+
// Cache the animation data
63+
animationCache.set(animationFile, data)
64+
setAnimationData(data)
65+
})
66+
.catch((error) =>
67+
console.error("Error loading Lottie animation:", error),
68+
)
69+
}
70+
}, [animationFile])
71+
72+
// Set lottieReady to true after a small delay when animation data is available
73+
useEffect(() => {
74+
if (animationData) {
75+
const timer = setTimeout(() => {
76+
setLottieReady(true)
77+
}, 300) // Small delay to ensure Lottie is rendered before fading in
78+
return () => clearTimeout(timer)
79+
}
80+
}, [animationData])
81+
2282
return (
2383
<div
2484
className={cn(
2585
"w-full h-full flex items-center justify-center bg-background",
2686
className,
2787
)}
2888
>
29-
<div className={cn("relative", sizeClasses[size])}>
30-
<div className="absolute inset-0 bg-foreground/30 rounded-full animate-pulse-slow"></div>
31-
<div className="absolute inset-0 bg-foreground rounded-full animate-pulse-fast"></div>
89+
<div
90+
className={cn(
91+
sizeMap[size].className,
92+
"flex items-center justify-center relative",
93+
)}
94+
>
95+
{/* Always render both with absolute positioning for smooth transition */}
96+
<div
97+
className={cn(
98+
"w-full h-full animate-spin-slow absolute inset-0 transition-opacity duration-300",
99+
lottieReady ? "opacity-0" : "opacity-100",
100+
)}
101+
>
102+
<svg
103+
width="100%"
104+
height="100%"
105+
viewBox="0 0 60 60"
106+
fill="none"
107+
xmlns="http://www.w3.org/2000/svg"
108+
aria-label="21st logo loading"
109+
>
110+
<path
111+
d="M0 20C0 8.95431 8.95431 0 20 0C31.0457 0 40 8.95431 40 20C40 31.0457 31.5 35.5 20 40H40C40 51.0457 31.0457 60 20 60C8.95431 60 0 51.0457 0 40C0 28.9543 9.5 22 20 20H0Z"
112+
fill={logoFill}
113+
/>
114+
<path
115+
d="M40 60C51.7324 55.0977 60 43.5117 60 30C60 16.4883 51.7324 4.90234 40 0V60Z"
116+
fill={logoFill}
117+
/>
118+
</svg>
119+
</div>
120+
121+
{animationData && (
122+
<div
123+
className={cn(
124+
"w-full h-full absolute inset-0 transition-opacity duration-300",
125+
lottieReady ? "opacity-100" : "opacity-0",
126+
)}
127+
>
128+
<Lottie
129+
animationData={animationData}
130+
loop={true}
131+
style={{ width: "100%", height: "100%" }}
132+
rendererSettings={{
133+
preserveAspectRatio: "xMidYMid slice",
134+
progressiveLoad: true,
135+
}}
136+
/>
137+
</div>
138+
)}
32139
</div>
33-
{showText && <span className="ml-3 text-foreground">{text}</span>}
34140
</div>
35141
)
36142
}
37143

38144
export const LoadingSpinnerPage = ({
39-
text,
40145
size,
41-
showText,
42146
className,
147+
animationFile,
148+
logoFill,
43149
}: LoadingSpinnerProps) => (
44150
<div
45151
className={cn(
46152
"w-full h-screen flex items-center justify-center bg-background",
47153
className,
48154
)}
49155
>
50-
<LoadingSpinner text={text} size={size} showText={showText} />
156+
<LoadingSpinner
157+
size={size}
158+
animationFile={animationFile}
159+
logoFill={logoFill}
160+
/>
51161
</div>
52162
)

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"fs": "0.0.1-security",
7676
"geist": "^1.3.1",
7777
"lodash": "^4.17.21",
78+
"lottie-react": "^2.4.1",
7879
"lucide-react": "^0.446.0",
7980
"micro": "^10.0.1",
8081
"monaco-jsx-highlighter": "^2.77.77",

apps/web/public/loading.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

pnpm-lock.yaml

Lines changed: 18 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)