Add loading indicator for admin asset initialization

Introduced a "loading" div to indicate when admin assets are being fetched. Updated rendering logic and styles in related components to account for this state. Prepared groundwork for potential view transitions.
This commit is contained in:
dswbx
2025-01-10 18:04:40 +01:00
parent bb756548a6
commit c7d983942f
5 changed files with 71 additions and 21 deletions

View File

@@ -140,11 +140,13 @@ export class AdminController extends Controller {
}
}
const theme = configs.server.admin.color_scheme ?? "light";
return (
<Fragment>
{/* dnd complains otherwise */}
{html`<!DOCTYPE html>`}
<html lang="en" class={configs.server.admin.color_scheme ?? "light"}>
<html lang="en" class={theme}>
<head>
<meta charset="UTF-8" />
<meta
@@ -173,9 +175,24 @@ export class AdminController extends Controller {
</Fragment>
)}
</head>
<body>
<body style={{ backgroundColor: theme === "light" ? "#fff" : "#000" }}>
<div id="root" />
<div id="app" />
<div id="app">
<div
id="loading"
style={{
height: "100vh",
width: "100vw",
display: "flex",
justifyContent: "center",
alignItems: "center",
fontFamily: "monospace",
opacity: 0.3
}}
>
Initializing...
</div>
</div>
<script
dangerouslySetInnerHTML={{
__html: bknd_context

View File

@@ -84,7 +84,7 @@ const Skeleton = ({ theme = "light" }: { theme?: string }) => {
</header>
<AppShell.Content>
<div className="flex flex-col w-full h-full justify-center items-center">
<span className="font-mono opacity-30">Loading</span>
{/*<span className="font-mono opacity-30">Loading</span>*/}
</div>
</AppShell.Content>
</AppShell.Root>

View File

@@ -45,8 +45,9 @@ const useLocationFromRouter = (router) => {
export function Link({
className,
native,
onClick,
...props
}: { className?: string; native?: boolean } & LinkProps) {
}: { className?: string; native?: boolean; transition?: boolean } & LinkProps) {
const router = useRouter();
const [path, navigate] = useLocationFromRouter(router);
@@ -69,17 +70,28 @@ export function Link({
const absPath = absolutePath(path, router.base).replace("//", "/");
const active =
href.replace(router.base, "").length <= 1 ? href === absPath : isActive(absPath, href);
const a = useRoute(_href);
/*if (active) {
console.log("link", { a, path, absPath, href, to, active, router });
}*/
if (native) {
return <a className={`${active ? "active " : ""}${className}`} {...props} />;
}
const wouterOnClick = (e: any) => {
// prepared for view transition
/*if (props.transition !== false) {
e.preventDefault();
onClick?.(e);
document.startViewTransition(() => {
navigate(props.href ?? props.to, props);
});
}*/
};
return (
<WouterLink
// @ts-expect-error className is not typed on WouterLink
<WouterLink className={`${active ? "active " : ""}${className}`} {...props} />
className={`${active ? "active " : ""}${className}`}
{...props}
onClick={wouterOnClick}
/>
);
}

View File

@@ -64,9 +64,26 @@ export function useNavigate() {
(
url: string,
options?:
| { query?: object; absolute?: boolean; replace?: boolean; state?: any }
| {
query?: object;
absolute?: boolean;
replace?: boolean;
state?: any;
transition?: boolean;
}
| { reload: true }
) => {
const wrap = (fn: () => void) => {
fn();
// prepared for view transition
/*if (options && "transition" in options && options.transition === false) {
fn();
} else {
document.startViewTransition(fn);
}*/
};
wrap(() => {
if (options && "reload" in options) {
window.location.href = url;
return;
@@ -77,6 +94,7 @@ export function useNavigate() {
replace: options?.replace,
state: options?.state
});
});
},
location
] as const;

View File

@@ -10,7 +10,10 @@ function ClientApp() {
// Render the app
const rootElement = document.getElementById("app")!;
if (!rootElement.innerHTML) {
const shouldRender =
!rootElement.innerHTML ||
(rootElement.childElementCount === 1 && rootElement.firstElementChild?.id === "loading");
if (shouldRender) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>