feat: client side loading
This commit is contained in:
parent
463141981b
commit
19a1344d3d
@ -2,6 +2,7 @@ import { isYoutubeLink } from "@lib/string.ts";
|
|||||||
import { IconBrandYoutube } from "@components/icons.tsx";
|
import { IconBrandYoutube } from "@components/icons.tsx";
|
||||||
import { GenericResource } from "@lib/types.ts";
|
import { GenericResource } from "@lib/types.ts";
|
||||||
import { SmallRating } from "@components/Rating.tsx";
|
import { SmallRating } from "@components/Rating.tsx";
|
||||||
|
import { Link } from "@islands/Link.tsx";
|
||||||
|
|
||||||
export function Card(
|
export function Card(
|
||||||
{
|
{
|
||||||
@ -22,7 +23,7 @@ export function Card(
|
|||||||
rating?: number;
|
rating?: number;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const backgroundStyle = {
|
const backgroundStyle: preact.JSX.CSSProperties = {
|
||||||
backgroundSize: "cover",
|
backgroundSize: "cover",
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
};
|
};
|
||||||
@ -34,7 +35,7 @@ export function Card(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<Link
|
||||||
href={link}
|
href={link}
|
||||||
style={backgroundStyle}
|
style={backgroundStyle}
|
||||||
data-thumb={thumbnail}
|
data-thumb={thumbnail}
|
||||||
@ -88,7 +89,7 @@ export function Card(
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute inset-x-0 bottom-0 h-3/4" />
|
<div class="absolute inset-x-0 bottom-0 h-3/4" />
|
||||||
</a>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ import * as $KMenu_commands_create_recipe from "./islands/KMenu/commands/create_
|
|||||||
import * as $KMenu_commands_create_recommendations from "./islands/KMenu/commands/create_recommendations.ts";
|
import * as $KMenu_commands_create_recommendations from "./islands/KMenu/commands/create_recommendations.ts";
|
||||||
import * as $KMenu_commands_create_series from "./islands/KMenu/commands/create_series.ts";
|
import * as $KMenu_commands_create_series from "./islands/KMenu/commands/create_series.ts";
|
||||||
import * as $KMenu_types from "./islands/KMenu/types.ts";
|
import * as $KMenu_types from "./islands/KMenu/types.ts";
|
||||||
|
import * as $Link from "./islands/Link.tsx";
|
||||||
import * as $Recommendations from "./islands/Recommendations.tsx";
|
import * as $Recommendations from "./islands/Recommendations.tsx";
|
||||||
import * as $Search from "./islands/Search.tsx";
|
import * as $Search from "./islands/Search.tsx";
|
||||||
import type { Manifest } from "$fresh/server.ts";
|
import type { Manifest } from "$fresh/server.ts";
|
||||||
@ -125,6 +126,7 @@ const manifest = {
|
|||||||
$KMenu_commands_create_recommendations,
|
$KMenu_commands_create_recommendations,
|
||||||
"./islands/KMenu/commands/create_series.ts": $KMenu_commands_create_series,
|
"./islands/KMenu/commands/create_series.ts": $KMenu_commands_create_series,
|
||||||
"./islands/KMenu/types.ts": $KMenu_types,
|
"./islands/KMenu/types.ts": $KMenu_types,
|
||||||
|
"./islands/Link.tsx": $Link,
|
||||||
"./islands/Recommendations.tsx": $Recommendations,
|
"./islands/Recommendations.tsx": $Recommendations,
|
||||||
"./islands/Search.tsx": $Search,
|
"./islands/Search.tsx": $Search,
|
||||||
},
|
},
|
||||||
|
42
islands/Link.tsx
Normal file
42
islands/Link.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { useEffect } from "preact/hooks";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// deno-lint-ignore no-var
|
||||||
|
var loadingTimeout: ReturnType<typeof setTimeout>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Link(
|
||||||
|
{ href, children, class: _class, style }: {
|
||||||
|
href?: string;
|
||||||
|
class?: string;
|
||||||
|
style?: preact.JSX.CSSProperties;
|
||||||
|
children: preact.ComponentChildren;
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
function handleClick() {
|
||||||
|
if (globalThis.loadingTimeout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
globalThis.loadingTimeout = setTimeout(() => {
|
||||||
|
document.querySelector("main")?.classList.add("loading");
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
clearTimeout(globalThis.loadingTimeout);
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelector("main")?.classList.remove("loading");
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={href}
|
||||||
|
style={style}
|
||||||
|
onClick={handleClick}
|
||||||
|
class={_class}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
@ -1,14 +1,15 @@
|
|||||||
import { PageProps } from "$fresh/server.ts";
|
import { PageProps } from "$fresh/server.ts";
|
||||||
|
import { Partial } from "$fresh/runtime.ts";
|
||||||
|
import { useEffect } from "preact/hooks";
|
||||||
|
|
||||||
import { Head } from "$fresh/runtime.ts";
|
|
||||||
export default function App({ Component }: PageProps) {
|
export default function App({ Component }: PageProps) {
|
||||||
const globalCss = Deno
|
const globalCss = Deno
|
||||||
.readTextFileSync("./static/global.css")
|
.readTextFileSync("./static/global.css")
|
||||||
.replaceAll("\n", "");
|
.replaceAll("\n", "");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<html>
|
||||||
<Head>
|
<head>
|
||||||
<link rel="stylesheet" href="/prism-material-dark.css" />
|
<link rel="stylesheet" href="/prism-material-dark.css" />
|
||||||
<link rel="stylesheet" href="/styles.css" />
|
<link rel="stylesheet" href="/styles.css" />
|
||||||
<link
|
<link
|
||||||
@ -21,9 +22,13 @@ export default function App({ Component }: PageProps) {
|
|||||||
<meta name="theme-color" content="#141218" />
|
<meta name="theme-color" content="#141218" />
|
||||||
<style>{globalCss}</style>
|
<style>{globalCss}</style>
|
||||||
<title>Memorium</title>
|
<title>Memorium</title>
|
||||||
</Head>
|
</head>
|
||||||
<Component />
|
<body f-client-nav>
|
||||||
|
<Partial name="body">
|
||||||
|
<Component />
|
||||||
|
</Partial>
|
||||||
|
</body>
|
||||||
<script src="/thumbnails.js" type="module" async defer />
|
<script src="/thumbnails.js" type="module" async defer />
|
||||||
</>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { PageProps } from "$fresh/server.ts";
|
import { PageProps } from "$fresh/server.ts";
|
||||||
import { resources } from "@lib/resources.ts";
|
import { resources } from "@lib/resources.ts";
|
||||||
|
import { Link } from "@islands/Link.tsx";
|
||||||
import { Emoji } from "@components/Emoji.tsx";
|
import { Emoji } from "@components/Emoji.tsx";
|
||||||
|
|
||||||
export default function MyLayout({ Component }: PageProps) {
|
export default function MyLayout({ Component }: PageProps) {
|
||||||
@ -12,12 +13,12 @@ export default function MyLayout({ Component }: PageProps) {
|
|||||||
<nav class="min-h-fit rounded-3xl p-3 grid gap-3 fixed t-0">
|
<nav class="min-h-fit rounded-3xl p-3 grid gap-3 fixed t-0">
|
||||||
{Object.values(resources).map((m) => {
|
{Object.values(resources).map((m) => {
|
||||||
return (
|
return (
|
||||||
<a
|
<Link
|
||||||
href={m.link}
|
href={m.link}
|
||||||
class={`flex items-center gap-2 text-white data-[current]:bg-white data-[current]:text-black p-3 text-xl w-full rounded-2xl`}
|
class="flex items-center gap-2 text-white data-[current]:bg-white data-[current]:text-black p-3 text-xl w-full rounded-2xl"
|
||||||
>
|
>
|
||||||
{<Emoji class="w-6 h-6" name={m.emoji} />} {m.name}
|
<Emoji class="w-6 h-6" name={m.emoji} /> {m.name}
|
||||||
</a>
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -8,6 +8,7 @@ import { RedirectSearchHandler } from "@islands/Search.tsx";
|
|||||||
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||||
import { GenericResource } from "@lib/types.ts";
|
import { GenericResource } from "@lib/types.ts";
|
||||||
import { ResourceCard } from "@components/Card.tsx";
|
import { ResourceCard } from "@components/Card.tsx";
|
||||||
|
import { Link } from "@islands/Link.tsx";
|
||||||
|
|
||||||
export const handler: Handlers<
|
export const handler: Handlers<
|
||||||
{ articles: Article[] | null; searchResults?: GenericResource[] }
|
{ articles: Article[] | null; searchResults?: GenericResource[] }
|
||||||
@ -35,13 +36,13 @@ export default function Greet(
|
|||||||
searchResults={searchResults}
|
searchResults={searchResults}
|
||||||
>
|
>
|
||||||
<header class="flex gap-4 items-center mb-5 md:hidden">
|
<header class="flex gap-4 items-center mb-5 md:hidden">
|
||||||
<a
|
<Link
|
||||||
class="px-4 ml-4 py-2 bg-gray-300 text-gray-800 rounded-lg flex items-center gap-1"
|
class="px-4 ml-4 py-2 bg-gray-300 text-gray-800 rounded-lg flex items-center gap-1"
|
||||||
href="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<IconArrowLeft class="w-5 h-5" />
|
<IconArrowLeft class="w-5 h-5" />
|
||||||
Back
|
Back
|
||||||
</a>
|
</Link>
|
||||||
|
|
||||||
<h3 class="text-2xl text-white font-light">📝 Articles</h3>
|
<h3 class="text-2xl text-white font-light">📝 Articles</h3>
|
||||||
</header>
|
</header>
|
||||||
|
@ -120,3 +120,11 @@ input[type=number] {
|
|||||||
.list-decimal li::marker {
|
.list-decimal li::marker {
|
||||||
color: #8a898c;
|
color: #8a898c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
main.loading {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user