Compare commits
No commits in common. "f3d9084c56299d782800097357cec50d982fe1e6" and "e31401b403a3eb19cb775c167d465a9155b215eb" have entirely different histories.
f3d9084c56
...
e31401b403
116
app/components/SmoothScroll.tsx
Normal file
116
app/components/SmoothScroll.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import type { JSX } from "solid-js";
|
||||
import { createSignal, onCleanup } from "solid-js";
|
||||
|
||||
type SmoothScrollProps = JSX.IntrinsicElements["div"] & {
|
||||
children: JSX.Element;
|
||||
};
|
||||
|
||||
export function SmoothScroll(props: SmoothScrollProps) {
|
||||
const [isScrolling, setIsScrolling] = createSignal(false);
|
||||
let animationFrameId: number | null = null;
|
||||
|
||||
const easeOutQuad = (t: number, b: number, c: number, d: number) => {
|
||||
const time = t / d;
|
||||
return -c * time * (time - 2) + b;
|
||||
};
|
||||
|
||||
const smoothScroll = (deltaY: number) => {
|
||||
const scrollSpeed = 3;
|
||||
const currentScroll = window.scrollY;
|
||||
const targetScroll = deltaY * scrollSpeed;
|
||||
const duration = 300;
|
||||
const startTime = performance.now();
|
||||
|
||||
const animateScroll = (currentTime: number) => {
|
||||
const elapsedTime = currentTime - startTime;
|
||||
const ease = easeOutQuad(
|
||||
elapsedTime,
|
||||
currentScroll,
|
||||
targetScroll,
|
||||
duration,
|
||||
);
|
||||
|
||||
window.scrollTo(0, ease);
|
||||
|
||||
if (elapsedTime < duration) {
|
||||
animationFrameId = requestAnimationFrame(animateScroll);
|
||||
} else {
|
||||
setIsScrolling(false);
|
||||
}
|
||||
};
|
||||
|
||||
animationFrameId = requestAnimationFrame(animateScroll);
|
||||
};
|
||||
|
||||
const isMobile = () => {
|
||||
const regex =
|
||||
/Mobi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
||||
return regex.test(navigator.userAgent);
|
||||
};
|
||||
|
||||
const isElementScrollable = (element: HTMLElement) => {
|
||||
if (!element) return false;
|
||||
|
||||
return (
|
||||
element.scrollHeight > element.clientHeight && element.tagName !== "HTML"
|
||||
);
|
||||
};
|
||||
|
||||
const findScrollableParent = (element: HTMLElement) => {
|
||||
let currentElement: HTMLElement | null = element;
|
||||
|
||||
while (currentElement) {
|
||||
if (isElementScrollable(currentElement)) {
|
||||
return currentElement;
|
||||
}
|
||||
|
||||
currentElement = currentElement.parentElement;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const handleWheel = (event: WheelEvent) => {
|
||||
if (isMobile()) return;
|
||||
if (event.ctrlKey) return;
|
||||
if (event.metaKey) return;
|
||||
|
||||
const hoveredElement = document.elementFromPoint(
|
||||
event.clientX,
|
||||
event.clientY,
|
||||
) as HTMLElement;
|
||||
|
||||
if (findScrollableParent(hoveredElement)) {
|
||||
if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (isScrolling()) {
|
||||
if (animationFrameId !== null) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
smoothScroll(event.deltaY);
|
||||
setIsScrolling(false);
|
||||
});
|
||||
|
||||
setIsScrolling(true);
|
||||
};
|
||||
|
||||
onCleanup(() => {
|
||||
if (animationFrameId !== null) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div {...props} onWheel={handleWheel} style={{ "touch-action": "auto" }}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import type { JSXElement } from "solid-js";
|
||||
|
||||
import { usePageContext } from "vike-solid/usePageContext";
|
||||
import { SmoothScroll } from "@/components/SmoothScroll";
|
||||
import { HeroSection } from "@/partials/HeroSection";
|
||||
import { clientOnly } from "vike-solid/clientOnly";
|
||||
import { Navigation } from "@/partials/Navigation";
|
||||
@ -11,42 +12,44 @@ import { Toaster } from "solid-toast";
|
||||
|
||||
import "./tailwind.css";
|
||||
|
||||
const ReadProgressBar = clientOnly(async () => (await import("@/partials/ReadProgressBar")).ReadProgressBar);
|
||||
const ReadProgressBar = clientOnly(
|
||||
async () => (await import("@/partials/ReadProgressBar")).ReadProgressBar,
|
||||
);
|
||||
|
||||
type DefaultLayoutProps = {
|
||||
children: JSXElement;
|
||||
children: JSXElement;
|
||||
};
|
||||
|
||||
export default function DefaultLayout(props: DefaultLayoutProps) {
|
||||
const pageContext = usePageContext();
|
||||
const pageContext = usePageContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="flex w-full flex-col font-sans">
|
||||
<Header />
|
||||
<ReadProgressBar />
|
||||
return (
|
||||
<>
|
||||
<SmoothScroll class="flex w-full flex-col font-sans">
|
||||
<Header />
|
||||
<ReadProgressBar />
|
||||
|
||||
{pageContext.urlPathname === "/" && <HeroSection />}
|
||||
{pageContext.urlPathname === "/" && <HeroSection />}
|
||||
|
||||
<div class="relative mx-auto w-full flex max-w-8xl flex-auto justify-center sm:px-2 lg:px-8 xl:px-12">
|
||||
<div class="hidden lg:relative lg:block lg:flex-none">
|
||||
<div class="absolute inset-y-0 right-0 w-[50vw] bg-slate-50" />
|
||||
<div class="absolute top-16 right-0 bottom-0 hidden h-12 w-px bg-linear-to-t from-slate-800" />
|
||||
<div class="absolute top-28 right-0 bottom-0 hidden w-px bg-slate-800" />
|
||||
<div class="sticky top-[4.75rem] -ml-0.5 h-[calc(100vh-4.75rem)] w-64 overflow-x-hidden overflow-y-auto py-16 pr-8 pl-0.5 xl:w-72 xl:pr-16">
|
||||
<Navigation />
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative mx-auto w-full flex max-w-8xl flex-auto justify-center sm:px-2 lg:px-8 xl:px-12">
|
||||
<div class="hidden lg:relative lg:block lg:flex-none">
|
||||
<div class="absolute inset-y-0 right-0 w-[50vw] bg-slate-50" />
|
||||
<div class="absolute top-16 right-0 bottom-0 hidden h-12 w-px bg-linear-to-t from-slate-800" />
|
||||
<div class="absolute top-28 right-0 bottom-0 hidden w-px bg-slate-800" />
|
||||
<div class="sticky top-[4.75rem] -ml-0.5 h-[calc(100vh-4.75rem)] w-64 overflow-x-hidden overflow-y-auto py-16 pr-8 pl-0.5 xl:w-72 xl:pr-16">
|
||||
<Navigation />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<DocsLayout>{props.children}</DocsLayout>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<DocsLayout>{props.children}</DocsLayout>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
<Footer />
|
||||
</SmoothScroll>
|
||||
|
||||
<Toaster />
|
||||
</>
|
||||
);
|
||||
<Toaster />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -15,38 +15,53 @@ import path from "node:path";
|
||||
type RemoveCommentRules = (root: Root) => void;
|
||||
|
||||
const removeCommentRules: RemoveCommentRules = (root) => {
|
||||
root.walkComments((comment) => {
|
||||
comment.remove();
|
||||
});
|
||||
root.walkComments((comment) => {
|
||||
comment.remove();
|
||||
});
|
||||
};
|
||||
|
||||
const __dirname = path.resolve();
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
prismjsVitePlugin({
|
||||
languages: ["javascript", "typescript", "tsx", "jsx", "css", "html", "bash", "nginx", "sql"],
|
||||
}),
|
||||
vike(),
|
||||
vikeSolid(),
|
||||
mdx({
|
||||
jsxImportSource: "solid-jsx",
|
||||
remarkPlugins: [remarkFrontmatter, remarkHeadingId, remarkExtractFrontmatter],
|
||||
}),
|
||||
tailwindcss(),
|
||||
telefunc(),
|
||||
],
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [...(process.env.NODE_ENV === "production" ? [removeCommentRules] : [])],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: "es2022",
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": __dirname,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
prismjsVitePlugin({
|
||||
languages: [
|
||||
"javascript",
|
||||
"typescript",
|
||||
"tsx",
|
||||
"jsx",
|
||||
"css",
|
||||
"html",
|
||||
"bash",
|
||||
"nginx",
|
||||
],
|
||||
}),
|
||||
vike(),
|
||||
vikeSolid(),
|
||||
mdx({
|
||||
jsxImportSource: "solid-jsx",
|
||||
remarkPlugins: [
|
||||
remarkFrontmatter,
|
||||
remarkHeadingId,
|
||||
remarkExtractFrontmatter,
|
||||
],
|
||||
}),
|
||||
tailwindcss(),
|
||||
telefunc(),
|
||||
],
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [
|
||||
...(process.env.NODE_ENV === "production" ? [removeCommentRules] : []),
|
||||
],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: "es2022",
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": __dirname,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user