Skip to content

Commit

Permalink
feat: layout + responsivenes improvements + custom scroll hook
Browse files Browse the repository at this point in the history
  • Loading branch information
vucinatim committed May 15, 2024
1 parent b935723 commit 4a8090e
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 28 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.51.4",
"react-icons": "^5.2.1",
"react-markdown": "^9.0.1",
"remark-gfm": "^4.0.0",
"sonner": "^1.4.41",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"tailwindcss/typography": "^3.1.0",
"zod": "^3.23.8"
},
"devDependencies": {
Expand Down
22 changes: 12 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 30 additions & 14 deletions src/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable react/jsx-no-literals */
import LanguageSwitcher from '@/components/common/language-switcher';
import Navbar from '@/components/common/navbar';
import ThemeToggle from '@/components/common/theme-toggle';
import ExampleCard from '@/components/examples/example-card';
import { FormExample } from '@/components/examples/form-example';
Expand All @@ -11,6 +12,7 @@ import { BackgroundBeams } from '@/components/ui/background-beams';
import env from '@/env';
import { getScopedI18n } from '@/i18n/server';
import path from 'path';
import { FaGithub } from 'react-icons/fa';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import readme from '../../../docs/README.md';
Expand All @@ -19,19 +21,29 @@ export default async function Home() {
const t = await getScopedI18n('home');

return (
<main className="flex h-screen flex-col items-stretch gap-y-8 p-8">
<main className="flex flex-col items-stretch gap-y-8 py-4">
<BackgroundBeams />
<div className="container flex items-center justify-end gap-x-8 px-0">
<LanguageSwitcher />
<ThemeToggle />
</div>
<div className="container relative grow">
<div className="absolute inset-0 grid grid-cols-3 gap-x-8">
<div className="col-span-2 flex flex-col gap-y-4 pr-20">
<Navbar>
<a
className="justify-self-center"
target="_blank"
href="https://github.com/zerodays/nextjs-template"
rel="noreferrer">
<FaGithub className="h-8 w-8 transition-colors hover:text-foreground/70" />
</a>
<div className="flex items-center gap-x-4">
<LanguageSwitcher />
<ThemeToggle />
</div>
</Navbar>

<div className="container relative grow pb-20">
<div className="flex flex-col gap-8 lg:grid lg:grid-cols-3">
<div className="flex flex-col gap-y-4 lg:col-span-2">
<h1 className="text-5xl font-bold">{t('title')}</h1>
<p className="font-mono">{t('subtitle')}</p>
<div className="relative grow">
<div className="scrollbar-none absolute inset-0 flex flex-col overflow-auto pt-8">
<div className="scrollbar-none flex flex-col pt-8 lg:overflow-auto">
<Markdown
className="prose prose-zinc dark:prose-invert min-w-full"
remarkPlugins={[remarkGfm]}>
Expand All @@ -40,7 +52,7 @@ export default async function Home() {
</div>
</div>
</div>
<div className="scrollbar-none flex h-full flex-col gap-y-8 overflow-auto">
<div className="scrollbar-none flex h-full flex-col gap-y-8 lg:overflow-auto">
<ExampleCard
title="✅ Form example"
subtitle={
Expand Down Expand Up @@ -75,7 +87,7 @@ export default async function Home() {
<ToastExample />
</ExampleCard>
<ExampleCard
title="🔗 Zodios server example"
title="🔗 Zodios use on server example"
subtitle={
<p>
Check out{' '}
Expand All @@ -86,7 +98,7 @@ export default async function Home() {
<ZodiosServerExample />
</ExampleCard>
<ExampleCard
title="🔗 Zodios client example"
title="🔗 Zodios use on client example"
subtitle={
<p>
Check out{' '}
Expand All @@ -111,7 +123,8 @@ const CodeLink = ({
lineNumber?: number;
}) => {
let url = '';
if (env.NODE_ENV === 'development') {
const isDev = env.NODE_ENV === 'development';
if (isDev) {
const projectRoot = process.cwd();
const absolutePath = path.join(projectRoot, filePath);
url = `vscode://file/${absolutePath}:${lineNumber}`;
Expand All @@ -122,7 +135,10 @@ const CodeLink = ({

return (
<span className="mx-1 rounded-lg bg-foreground px-2 text-sm font-semibold text-background transition-colors hover:bg-foreground/70">
<a href={url} rel="noopener noreferrer">
<a
target={!isDev ? '_blank' : undefined}
href={url}
rel="noopener noreferrer">
{fileName}
</a>
</span>
Expand Down
29 changes: 29 additions & 0 deletions src/components/common/navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client';

import useScrollCallback from '@/lib/hooks/use-scroll-callback';
import { cn } from '@/lib/utils';
import { useState } from 'react';

interface NavbarProps {
children: React.ReactNode;
}

const Navbar = ({ children }: NavbarProps) => {
const [isAtTop, setIsAtTop] = useState(true);
useScrollCallback({
callback: (offsetTop) => {
setIsAtTop(offsetTop < 10);
},
});
return (
<div
className={cn(
'container sticky top-0 z-10 flex items-center justify-between gap-x-8 px-8 py-4 transition-colors',
!isAtTop && 'bg-gradient-to-b from-background to-transparent',
)}>
{children}
</div>
);
};

export default Navbar;
6 changes: 6 additions & 0 deletions src/components/examples/sentry-example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@

import { Button } from '@/components/ui/button';
import * as Sentry from '@sentry/nextjs';
import { toast } from 'sonner';

const SentryExample = () => {
return (
<Button
variant="outline"
onClick={() =>
Sentry.startSpan(
{
name: 'Example Frontend Span',
op: 'test',
},
() => {
toast('Sentry Error has been thrown', {
description:
'Check Sentry for more details. In development mode sentry is disabled (has no SENTRY_DSN env variable set) by default.',
});
throw new Error('Sentry Example Frontend Error');
},
)
Expand Down
1 change: 1 addition & 0 deletions src/components/examples/toast-example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { toast } from 'sonner';
const ToastExample = () => {
return (
<Button
variant="outline"
onClick={() =>
toast('Event has been created', {
description: 'Sunday, December 03, 2023 at 9:00 AM',
Expand Down
5 changes: 3 additions & 2 deletions src/components/examples/zodios-client-example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const ZodiosClientExample = () => {
</PopoverContent>
</Popover>
<Button
variant="outline"
onClick={() =>
mutate({
name: 'Example',
Expand All @@ -91,13 +92,13 @@ const ZodiosClientExample = () => {
Trigger Error
</Button>
</div>
<div className="flex h-48 items-center justify-center overflow-hidden rounded-lg bg-black">
<div className="flex h-44 items-center justify-center">
{isLoading || isRefetching ? (
<div>Loading...</div>
) : (
<textarea
readOnly
className="h-full w-full p-4 text-sm"
className="h-full w-full rounded-lg p-4 text-sm focus:outline-foreground"
value={data && JSON.stringify(data[0], null, 2)}
/>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/components/examples/zodios-server-example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const ZodiosServerExample = async () => {
</p>
<textarea
readOnly
className="h-48 rounded-lg p-4 text-sm"
className="h-44 rounded-lg p-4 text-sm focus:outline-foreground"
value={JSON.stringify(data, null, 2)}
/>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/background-beams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export const BackgroundBeams = React.memo(
</radialGradient>
</defs>
</svg>
<div className="absolute inset-x-0 bottom-0 h-40 bg-gradient-to-b from-transparent to-background" />
</div>
);
},
Expand Down
31 changes: 31 additions & 0 deletions src/lib/hooks/use-scroll-callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect } from 'react';

interface UseScrollCallbackProps {
ref?: React.RefObject<HTMLElement>;
callback: (offsetTop: number) => void;
}

const useScrollCallback = ({ ref, callback }: UseScrollCallbackProps) => {
useEffect(() => {
// Determine the scrollable element
const scrollableElement = ref?.current || window;

const handleScroll = () => {
if (scrollableElement instanceof Window) {
// Handle scroll for window
callback(window.scrollY || document.documentElement.scrollTop);
} else if (scrollableElement instanceof HTMLElement) {
// Handle scroll for HTMLElement
callback(scrollableElement.scrollTop);
}
};

scrollableElement.addEventListener('scroll', handleScroll);

return () => {
scrollableElement.removeEventListener('scroll', handleScroll);
};
}, [callback, ref]);
};

export default useScrollCallback;

0 comments on commit 4a8090e

Please sign in to comment.