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

✨ add button component #132

Merged
merged 1 commit into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/[lang]/(home)/Article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function Article({
/>
)}
</div>
<div className="prose prose-zinc dark:prose-invert">
<div id={id} className="prose prose-zinc dark:prose-invert">
<H2>
<Link href={`/posts/${id}`}>{title}</Link>
</H2>
Expand Down
11 changes: 9 additions & 2 deletions src/app/[lang]/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import type { Metadata } from 'next';

import { getDictionary } from '~/data/i18n';
import Container from '@/components/Container';
import { H1 } from '@/components/Heading';
import { H1, H2 } from '@/components/Heading';
import Button from '@/components/Button';
import List from '@/components/List';
import { getAllDataFrontmatter } from '@/utils/mdx';

Expand All @@ -22,6 +23,7 @@ export async function generateMetadata({
async function RootPage({ params: { lang } }: RootParams) {
const posts = await getAllDataFrontmatter('posts');
const { homePage, common } = await getDictionary(lang);
const nextPostId = posts.at(4)?.id || '';

return (
<>
Expand All @@ -30,8 +32,13 @@ async function RootPage({ params: { lang } }: RootParams) {
<p className="text-xl">{homePage.description}</p>
</Container>
<Container as="main" className="py-8">
<p className="mb-4 text-lg">{common.latestPosts}</p>
<H2 className="mb-6 text-center text-2xl">{common.latestPosts}</H2>
<List Item={Article} items={posts.slice(0, 4)} />
<div className="my-4 flex justify-center">
<Button href={`/posts#${nextPostId}`} className="text-lg">
{common.morePosts}
</Button>
</div>
</Container>
</>
);
Expand Down
8 changes: 1 addition & 7 deletions src/app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import ThemeSwitcher from '@/components/ThemeSwitcher';
import Header, { Avatar } from './Header';
import Footer from './Footer';
import Menu, { MenuProps } from './Menu';
import cn from '@/utils/cn';

export async function generateStaticParams() {
return locales.map((lang) => ({ lang }));
Expand Down Expand Up @@ -60,12 +59,7 @@ async function I18nLayout({
<>
<Header avatar={<Avatar src={avatar.src} alt={avatar.alt} />}>
<Menu menu={menu} />
<ThemeSwitcher
className={cn(
'neon-box rounded-full p-3 backdrop-blur-sm',
'bg-zinc-100/80 dark:bg-zinc-900/80'
)}
/>
<ThemeSwitcher />
</Header>
{children}
<Footer copyright={copyright} />
Expand Down
14 changes: 10 additions & 4 deletions src/app/[lang]/posts/InfiniteList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { memo } from 'react';
import { useSearchParams } from 'next/navigation';
import Link from '@/components/Link';
import Button from '@/components/Button';
import List from '@/components/List';
import { clamp } from '@/utils/math';
import Article from '../(home)/Article';
Expand All @@ -24,9 +24,15 @@ function InfiniteList({ items, morePostsText }: InfiniteListProps) {
<>
<List Item={MemoArticle} items={items.slice(0, limit)} />
{limit < total && (
<Link href={`?limit=${clampLimit(limit + 10)}`} replace scroll={false}>
{morePostsText}
</Link>
<div className="my-4 flex justify-center">
<Button
href={`?limit=${clampLimit(limit + 10)}`}
scroll={false}
replace
>
{morePostsText}
</Button>
</div>
)}
</>
);
Expand Down
39 changes: 39 additions & 0 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client';

import type { AriaAttributes, ButtonHTMLAttributes } from 'react';
import cn from '@/utils/cn';
import Link, { LinkProps } from './Link';

type BaseButtonProps = {
href?: never;
} & ButtonHTMLAttributes<HTMLButtonElement>;

type ButtonProps = BaseButtonProps | LinkProps;

function Button({
children,
className,
...props
}: ButtonProps & AriaAttributes) {
const buttonOrLinkClassName = cn(
'neon-box rounded-full px-5 py-1.5 backdrop-blur-sm',
'bg-zinc-100/80 dark:bg-zinc-900/80',
className
);

if (props.href) {
return (
<Link className={buttonOrLinkClassName} {...props}>
{children}
</Link>
);
}

return (
<button className={buttonOrLinkClassName} {...props}>
{children}
</button>
);
}

export default Button;
2 changes: 1 addition & 1 deletion src/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { HiExternalLink } from 'react-icons/hi';
import cn from '@/utils/cn';
import getLocale from '@/utils/getLocale';

type LinkProps<T extends string = string> = (
export type LinkProps<T extends string = string> = (
| NextLinkProps<T>
| LinkWithoutLocalePathProps
| ExternalLinkProps
Expand Down
21 changes: 5 additions & 16 deletions src/components/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@
import { useTheme } from 'next-themes';
import { BsSunFill, BsMoonFill } from 'react-icons/bs';
import useIsMounted from '@/hooks/useIsMounted';
import Button from './Button';

type ThemeSwitcherProps = {
className?: string;
};

function ThemeSwitcher({ className }: ThemeSwitcherProps) {
function ThemeSwitcher() {
const isMounted = useIsMounted();
const { resolvedTheme, setTheme } = useTheme();
const isDarkTheme = resolvedTheme === 'dark';
Expand All @@ -18,25 +15,17 @@ function ThemeSwitcher({ className }: ThemeSwitcherProps) {
};

if (!isMounted) {
return (
<div className={className}>
<div className="h-5 w-5 animate-spin rounded-full border-2 border-white/80 border-t-white/20"></div>
</div>
);
return <div></div>;
}

return (
<button
aria-label="theme switcher"
className={className}
onClick={handleClick}
>
<Button aria-label="theme switcher" className="p-3" onClick={handleClick}>
{isDarkTheme ? (
<BsMoonFill size="1.25rem" />
) : (
<BsSunFill size="1.25rem" />
)}
</button>
</Button>
);
}

Expand Down
4 changes: 3 additions & 1 deletion types/link.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ type DynamicRoutesWithoutLocalePath =
: never;

type LinkWithoutLocalePathProps = {
href: DynamicRoutesWithoutLocalePath;
href:
| DynamicRoutesWithoutLocalePath
| `${DynamicRoutesWithoutLocalePath}#${string}`;
};

type ExternalLinkProps = {
Expand Down
Loading