Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implemented skeletons loading page, for all navigation #570

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
184 changes: 183 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta name="robots" content="index, follow, NOODP" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16">
<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16" />
<title>tscircuit - Code Electronics with React</title>
<meta name="description" content="tscircuit is an open-source electronics design tool that lets you create circuits using React components. Design schematics, generate PCB layouts, export and manufacture PCBs online!" />
<meta name="keywords" content="electronic design, PCB design, schematic capture, React components, circuit design, electronics CAD, open source EDA" />
Expand All @@ -15,9 +15,191 @@
<meta name="twitter:title" content="tscircuit - Design Electronics with React Components" />
<meta name="twitter:description" content="Create electronic circuits using React components. Free open-source electronics design tool." />
<link rel="canonical" href="https://tscircuit.com" />

<!-- Inline CSS for Skeleton Loader -->
<style>
/* General Reset */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
padding: 16px;
}

.shimmer {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite ease-in-out;
border-radius: 8px;
}

@keyframes shimmer {
0% {
background-position: -150%;
}
100% {
background-position: 150%;
}
}


.page-wrapper {
display: flex;
flex-direction: column;
gap: 16px;
height: 100vh;
}

.nav-skeleton {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 20px;
background: #ffffff;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}

.logo-placeholder {
width: 100px;
height: 40px;
}

.nav-items {
display: flex;
gap: 16px;
}

.nav-item {
width: 80px;
height: 20px;
}

.action-buttons {
display: flex;
gap: 12px;
}

.action-placeholder {
height: 40px;
border-radius: 8px;
}

.content-skeleton {
flex: 1;
display: flex;
flex-direction: row;
gap: 24px;
padding: 20px;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
overflow-y: auto;
}

.main-content {
flex: 3;
display: flex;
flex-direction: column;
gap: 24px;
}

.horizontal-line {
width: 100%;
height: 25px;
background: linear-gradient(90deg, #f5f5f5 25%, #eaeaea 50%, #f5f5f5 75%);
border-radius: 8px;
}

.box-container {
display: flex;
gap: 16px;
}

.box {
width: 100%;
height: 300px;
}

.sidebar {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
}

.sidebar-block {
width: 100%;
height: 80px;
}
</style>
</head>
<body>

<div id="skeleton-loader">

<div class="page-wrapper">

<div class="nav-skeleton">
<div class="logo-placeholder shimmer"></div>
<div class="nav-items">
<div class="nav-item shimmer"></div>
<div class="nav-item shimmer"></div>
<div class="nav-item shimmer"></div>
<div class="nav-item shimmer"></div>
</div>
<div class="action-buttons">
<div class="action-placeholder shimmer" style="width: 80px;"></div>
<div class="action-placeholder shimmer" style="width: 120px;"></div>
</div>
</div>


<div class="content-skeleton">
<div class="main-content">
<!-- Initial lines -->
<div class="horizontal-line shimmer" style="width: 90%;"></div>
<div class="horizontal-line shimmer" style="width: 80%;"></div>
<div class="horizontal-line shimmer" style="width: 70%;"></div>

<!-- Single Box -->
<div class="box-container">
<div class="box shimmer"></div>
</div>

<!-- Additional lines -->
<div class="horizontal-line shimmer" style="width: 80%;"></div>
<div class="horizontal-line shimmer" style="width: 75%;"></div>
<div class="horizontal-line shimmer" style="width: 65%;"></div>
<div class="horizontal-line shimmer" style="width: 70%;"></div>
<div class="horizontal-line shimmer" style="width: 60%;"></div>
<div class="horizontal-line shimmer" style="width: 60%;"></div>

<div class="horizontal-line shimmer" style="width: 60%;"></div>
</div>

<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-block shimmer"></div>
<div class="sidebar-block shimmer"></div>
<div class="sidebar-block shimmer"></div>
<div class="sidebar-block shimmer"></div>
</div>
</div>
</div>
</div>


<div id="root"></div>


<script type="module" src="/src/main.tsx"></script>


</body>
</html>
27 changes: 21 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,31 @@
import { ComponentType, Suspense, lazy } from "react"
import { ComponentType, Suspense, lazy, useEffect } from "react" // Added `useEffect` for handling skeleton cleanup
import { Toaster } from "@/components/ui/toaster"
import { Route, Switch } from "wouter"
import "./components/CmdKMenu"
import { ContextProviders } from "./ContextProviders"
import React from "react"

// Lazy loading helper
const lazyImport = (importFn: () => Promise<any>) =>
lazy<ComponentType<any>>(async () => {
try {
const module = await importFn()

if (module.default) {
return { default: module.default }
}

const pageExportNames = ["Page", "Component", "View"]
for (const suffix of pageExportNames) {
const keys = Object.keys(module).filter((key) => key.endsWith(suffix))
if (keys.length > 0) {
return { default: module[keys[0]] }
}
}

const componentExport = Object.values(module).find(
(exp) => typeof exp === "function" && exp.prototype?.isReactComponent,
)
if (componentExport) {
return { default: componentExport }
}

throw new Error(
`No valid React component found in module. Available exports: ${Object.keys(module).join(", ")}`,
)
Expand All @@ -38,6 +35,7 @@ const lazyImport = (importFn: () => Promise<any>) =>
}
})

// Lazy-loaded pages
const AiPage = lazyImport(() => import("@/pages/ai"))
const AuthenticatePage = lazyImport(() => import("@/pages/authorize"))
const DashboardPage = lazyImport(() => import("@/pages/dashboard"))
Expand Down Expand Up @@ -82,10 +80,27 @@ class ErrorBoundary extends React.Component<
}

function App() {
// Added useEffect to handle cleanup of the skeleton loader
useEffect(() => {
// Hide the skeleton from index.html when React mounts
const skeletonLoader = document.getElementById("skeleton-loader")
if (skeletonLoader) {
skeletonLoader.style.display = "none"
}
}, [])
const renderSkeleton = () => {
const skeletonLoader = document.getElementById("skeleton-loader")
if (skeletonLoader) {
return (
<div dangerouslySetInnerHTML={{ __html: skeletonLoader.outerHTML }} />
)
}
return null
}
return (
<ContextProviders>
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<Suspense fallback={renderSkeleton()}>
<Switch>
<Route path="/" component={LandingPage} />
<Route path="/editor" component={EditorPage} />
Expand Down
Loading