Skip to content

Commit

Permalink
feat: migrate with @zl-asica/react.
Browse files Browse the repository at this point in the history
- Fix UX of back to top button.
- Fix alt translation for friends page avatars.
- Fix showing author and date when friends/about page title customized.

- Migrate with @zl-asica/react package for hooks and some utils.
  • Loading branch information
ZL-Asica committed Nov 20, 2024
1 parent a11b6f9 commit 0487f91
Show file tree
Hide file tree
Showing 19 changed files with 69 additions and 185 deletions.
9 changes: 0 additions & 9 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,6 @@ const nextConfig: NextConfig = {
'slugify',
],
},
async redirects() {
return [
{
source: '/feed',
destination: '/feed.xml',
permanent: true,
},
];
},
};

export default nextConfig;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"prepare": "husky"
},
"dependencies": {
"@zl-asica/react": "^0.3.0",
"clsx": "^2.1.1",
"es-toolkit": "^1.27.0",
"gray-matter": "^4.0.3",
Expand Down
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

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

2 changes: 1 addition & 1 deletion posts/_pages/Friends.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: '' # (Optional) Default is Friends
title: Some Customized Title # (Optional) Default is Friends
thumbnail: '' # (Optional) You can put your personal about thumbnail
showComments: true # Set whether you want have comment for this page
---
Expand Down
5 changes: 4 additions & 1 deletion src/components/article/ArticlePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const ArticlePage = ({
title={post.frontmatter.title}
author={post.frontmatter.author}
date={post.frontmatter.date}
slug={post.slug}
/>
)}

Expand Down Expand Up @@ -127,15 +128,17 @@ const TitleHeader = ({
title,
author,
date,
slug,
}: {
title: string;
author: string;
date: string;
slug: string;
}) => {
return (
<div className='mx-auto mb-5 w-full max-w-3xl'>
<h1 className='text-3xl font-bold'>{title}</h1>
{includes(['about', 'friends'], lowerCase(title)) || (
{includes(['about', 'friends'], lowerCase(slug)) || (
<MetaInfo
author={author}
date={date}
Expand Down
9 changes: 5 additions & 4 deletions src/components/article/TOC.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use client';

import { FaListUl } from 'react-icons/fa';
import { useIsTop } from '@zl-asica/react';

import TOCLink from './TOCLink';

import { useTOCLogic, useVisibilityOnScroll } from '@/hooks';
import { useTOCLogic } from '@/hooks';

interface TOCProperties {
items: TocItems[];
Expand All @@ -19,17 +20,17 @@ const TOC = ({
autoSlug = true,
showThumbnail = true,
}: TOCProperties) => {
const { activeSlug, isOpen, handleToggle, handleLinkClick, tocReference } =
const { activeSlug, isOpen, toggleOpen, handleLinkClick, tocReference } =
useTOCLogic();
const { isVisible } = useVisibilityOnScroll(showThumbnail ? undefined : 0);
const isVisible = !useIsTop(showThumbnail ? 150 : 50);

return (
<div
className={`${isVisible ? 'opacity-100' : 'pointer-events-none opacity-0'} duration-200 ease-in-out`}
>
<button
hidden={!isVisible}
onClick={handleToggle}
onClick={toggleOpen}
aria-label={translation.post.tocToggle}
className={`fixed bottom-28 right-8 z-50 flex h-12 w-12 items-center justify-center rounded-full bg-[var(--sakuraPink)] p-3 text-white shadow-lg transition-transform md:right-16 lg:right-20 xl:hidden ${
isOpen ? 'translate-y-2' : 'hover:scale-110'
Expand Down
5 changes: 4 additions & 1 deletion src/components/article/parser/markdownComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,10 @@ const createMarkdownComponents = (
isValidElement(children) &&
children.props?.className === 'language-Links'
) {
return renderFriendLinks(children.props?.children as string);
return renderFriendLinks(
children.props?.children as string,
translation
);
}

const language =
Expand Down
17 changes: 5 additions & 12 deletions src/components/article/parser/renderCodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { nord } from 'react-syntax-highlighter/dist/esm/styles/hljs';
import { useState } from 'react';
import { copyToClipboard, useToggle } from '@zl-asica/react';
import SyntaxHighlighter from 'react-syntax-highlighter';

interface CodeBlockProperties {
Expand All @@ -11,23 +11,16 @@ interface CodeBlockProperties {
}

const CodeBlock = ({ match, translation, children }: CodeBlockProperties) => {
const [isCopied, setIsCopied] = useState(false);
const [isCopied, toggleCopied] = useToggle();
const cleanedChildren = String(children).replace(/\n$/, '');

// Copy code block to clipboard
const handleCopy = () => {
if (isCopied) return;
navigator.clipboard.writeText(cleanedChildren);
setIsCopied(true);
// Hide the 'Copied!' message after 3 seconds
setTimeout(() => setIsCopied(false), 3000);
};

return (
<div className='relative'>
{/* Copy button */}
<button
onClick={handleCopy}
onClick={async () => {
await copyToClipboard(cleanedChildren, toggleCopied, 3000);
}}
className='absolute -top-7 right-2 rounded bg-[var(--skyblue)] px-2 py-1 text-xs hover:bg-opacity-80'
>
{isCopied ? translation.post.copied : translation.post.copy}
Expand Down
7 changes: 5 additions & 2 deletions src/components/article/parser/renderFriendLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { replace } from 'es-toolkit/compat';
import Image from 'next/image';
import Link from 'next/link';

const renderFriendLinks = (linksChildren: string): JSX.Element => {
const renderFriendLinks = (
linksChildren: string,
transition: Translation
): JSX.Element => {
let links: FriendLink[] = [];
try {
links = JSON.parse(replace(linksChildren, /}\s*,\s*{/g, '},{'));
Expand Down Expand Up @@ -37,7 +40,7 @@ const renderFriendLinks = (linksChildren: string): JSX.Element => {
src={link.img || ''}
width={100}
height={100}
alt={`Avatar of ${link.title || ''}`}
alt={`${transition.friends.avatar}: ${link.title || ''}`}
className='h-[100px] w-[100px] rounded-full object-cover object-center'
priority={false}
/>
Expand Down
12 changes: 7 additions & 5 deletions src/components/common/BackToTop.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
'use client';

import { FaArrowUp } from 'react-icons/fa';

import { useVisibilityOnScroll } from '@/hooks';
import { useIsBottom, backToTop, useIsTop } from '@zl-asica/react';
import { usePathname } from 'next/navigation';

const BackToTop = () => {
const { isVisible, isAtBottom } = useVisibilityOnScroll();
const path = usePathname();
const isVisible = !useIsTop(150) && path !== '/';
const isBottom = useIsBottom(100);

return (
<button
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
onClick={backToTop()}
className={`${
isVisible ? 'opacity-100' : 'opacity-0'
} fixed bottom-8 right-8 z-50 flex h-12 w-12 items-center justify-center rounded-full bg-[var(--sakuraPink)] text-white shadow-lg transition-all duration-200 hover:scale-125 md:right-16 lg:right-20 xl:right-[calc((100vw-1280px)/2+10px)] ${
isAtBottom ? 'animate-bounce' : ''
isBottom ? 'bottom-12 scale-125' : ''
}`}
aria-label='Back to Top'
hidden={!isVisible}
Expand Down
19 changes: 8 additions & 11 deletions src/components/common/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
'use client';

import { usePathname } from 'next/navigation';
import { useRef, useState } from 'react';
import { useRef } from 'react';
import { FaAngleUp, FaBars } from 'react-icons/fa6';
import Link from 'next/link';
import { useClickOutside, useToggle, useScrollProgress } from '@zl-asica/react';

import HeaderMenu from './HeaderMenu';

import { useOutsideClick, useScrollProgress } from '@/hooks';

interface HeaderProperties {
config: Config;
}

const Header = ({ config }: HeaderProperties) => {
const [isOpen, setIsOpen] = useState(false);
const [isOpen, toggleOpen] = useToggle();
const siteTitle = config.title;
const translation = config.translation;
const scrollProgress = useScrollProgress();
const menuReference = useRef<HTMLDivElement>(null);
const pathname = usePathname();
const isHomePage = pathname === '/';

const toggleMenu = () => setIsOpen((previous) => !previous);

useOutsideClick(menuReference, () => {
if (isOpen) setIsOpen(false);
useClickOutside(menuReference, () => {
if (isOpen) toggleOpen();
});

return (
Expand All @@ -51,7 +48,7 @@ const Header = ({ config }: HeaderProperties) => {
{/* Mobile Menu Button */}
<button
className={`z-50 flex h-12 w-12 items-center justify-center rounded-full bg-[var(--foreground)] text-2xl text-[var(--background)] shadow-md transition-transform duration-300 hover:scale-110 md:hidden`}
onClick={toggleMenu}
onClick={toggleOpen}
aria-label='Toggle menu'
aria-expanded={isOpen}
aria-controls='mobile-menu'
Expand All @@ -71,15 +68,15 @@ const Header = ({ config }: HeaderProperties) => {
translation={translation}
isMobile
ulClassName='flex flex-col items-start gap-4 p-6'
onClickHandler={toggleMenu}
onClickHandler={toggleOpen}
/>
</div>

{/* Backdrop */}
{isOpen && (
<div
className='fixed inset-0 z-40 bg-black bg-opacity-50 transition-opacity duration-300'
onClick={toggleMenu}
onClick={toggleOpen}
aria-hidden
/>
)}
Expand Down
10 changes: 5 additions & 5 deletions src/components/posts/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { useState, useEffect, useRef } from 'react';
import { useSearchParams } from 'next/navigation';
import Form from 'next/form';
import { clsx } from 'clsx';
import { useClickOutside, useToggle } from '@zl-asica/react';

import { useOutsideClick } from '@/hooks';
import { validateParameters, updateURL } from '@/services/utils';

interface SearchInputProperties {
Expand All @@ -27,7 +27,7 @@ const SearchInput = ({
const [searchQuery, setSearchQuery] = useState(initialValue);
const [selectedCategory, setSelectedCategory] = useState('');
const [selectedTag, setSelectedTag] = useState('');
const [expanded, setExpanded] = useState(false);
const [expanded, toggleExpanded] = useToggle();

// Initialize search parameters
useEffect(() => {
Expand All @@ -41,9 +41,9 @@ const SearchInput = ({
}, [searchParameters, initialValue]);

// Close the form when clicking outside
useOutsideClick(formReference, () => {
useClickOutside(formReference, () => {
if (expanded && !selectedCategory && !selectedTag) {
setExpanded(false);
toggleExpanded();
}
});

Expand Down Expand Up @@ -95,7 +95,7 @@ const SearchInput = ({
placeholder={`🔍 ${translation.search.prompt}`}
value={searchQuery}
onChange={(event) => setSearchQuery(event.target.value)}
onFocus={() => setExpanded(true)}
onFocus={toggleExpanded}
className='w-full rounded-full border border-gray-300 px-4 py-2 pr-16 transition-all duration-300 focus:ring-2'
/>
<button
Expand Down
4 changes: 0 additions & 4 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
export { default as useDebouncedEvent } from './useDebouncedEvent';
export { default as useOutsideClick } from './useOutsideClick';
export { default as useScrollProgress } from './useScrollProgress';
export { default as useVisibilityOnScroll } from './useVisibilityOnScroll';
export { default as useTOCLogic } from './useTOCLogic';
37 changes: 0 additions & 37 deletions src/hooks/useDebouncedEvent.ts

This file was deleted.

19 changes: 0 additions & 19 deletions src/hooks/useOutsideClick.ts

This file was deleted.

Loading

0 comments on commit 0487f91

Please # to comment.