|
|
@@ -0,0 +1,27 @@
|
|
|
+"use client";
|
|
|
+
|
|
|
+import React from "react";
|
|
|
+
|
|
|
+import { cn } from "@/lib/utils";
|
|
|
+import { Skeleton } from "@/components/ui/skeleton";
|
|
|
+import { useDebouncedVisibility } from "@/lib/frontend/hooks/useDebouncedVisibility";
|
|
|
+
|
|
|
+/**
|
|
|
+ * DebouncedSkeleton
|
|
|
+ *
|
|
|
+ * Behavior:
|
|
|
+ * - Reserves layout space immediately (renders a plain div with sizing classes).
|
|
|
+ * - Shows the animated Skeleton only after delayMs.
|
|
|
+ *
|
|
|
+ * This avoids "skeleton flicker" on fast loads.
|
|
|
+ */
|
|
|
+export function DebouncedSkeleton({ className, delayMs = 200, ...props }) {
|
|
|
+ const visible = useDebouncedVisibility(true, { delayMs, minVisibleMs: 0 });
|
|
|
+
|
|
|
+ if (!visible) {
|
|
|
+ // Reserve space, but do not show animation/background yet.
|
|
|
+ return <div aria-hidden="true" className={cn(className)} />;
|
|
|
+ }
|
|
|
+
|
|
|
+ return <Skeleton className={className} {...props} />;
|
|
|
+}
|