You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
blog.krasnikov.pro/components/hero-section.tsx

123 lines
3.4 KiB

'use client';
import { useReducer } from 'react';
import GraphemeSplitter from 'grapheme-splitter';
import { Pause, Play } from 'lucide-react';
import Typist from 'react-typist-component';
import Balancer from 'react-wrap-balancer';
import { blogConfig } from '@/config';
import { cn } from '@/lib/utils';
type TypingState = {
titleDone: boolean;
subtitleDone: boolean;
isPaused: boolean;
};
type TypingAction =
| { type: 'togglePause' }
| { type: 'setDone'; payload: 'title' | 'subtitle' };
const reducer = (state: TypingState, action: TypingAction) => {
switch (action.type) {
case 'togglePause':
return {
...state,
isPaused: !state.isPaused,
};
case 'setDone':
return {
...state,
[`${action.payload}Done`]: true,
};
}
};
const splitter = (str: string) => new GraphemeSplitter().splitGraphemes(str);
export function HeroSection() {
const [{ titleDone, subtitleDone, isPaused }, dispatch] = useReducer(
reducer,
{
titleDone: false,
subtitleDone: false,
isPaused: false,
},
);
return (
<section
className={cn(
'flex flex-col items-center justify-center space-y-2',
'relative h-40 w-full rounded-md px-4 shadow-lg',
'bg-slate-300 dark:bg-slate-800/50',
)}
>
<Typist
typingDelay={100}
splitter={splitter}
pause={isPaused}
onTypingDone={() => dispatch({ type: 'setDone', payload: 'title' })}
>
<h1 className="block w-full text-center text-3xl font-bold text-slate-800 dark:text-rose-50 xs:text-4xl sm:text-5xl">
<Balancer>
Добро пожаловать
<span className="ml-2 inline-block origin-[70%_70%] animate-wave">
👋
</span>
</Balancer>
</h1>
</Typist>
<p className="text-center text-lg text-slate-800 dark:text-rose-50 xs:text-2xl">
{titleDone && (
<Typist
typingDelay={100}
startDelay={1000}
pause={isPaused}
onTypingDone={() => {
dispatch({ type: 'setDone', payload: 'subtitle' });
}}
>
Блог про {' '}
</Typist>
)}
{subtitleDone && (
<Typist typingDelay={100} backspaceDelay={75} pause={isPaused} loop>
{blogConfig.topics.map((topic) => (
<span key={topic} className="font-semibold">
{topic}
<Typist.Delay ms={1000} />
<Typist.Backspace count={topic.length} />
</span>
))}
</Typist>
)}
</p>
<button
className="absolute right-3 top-1"
onClick={() => dispatch({ type: 'togglePause' })}
>
{isPaused ? (
<Play
className={cn(
'h-4 w-4',
'text-slate-400/50 hover:text-accent',
'dark:text-rose-50/20 dark:hover:text-accent-dark',
)}
aria-label="Запустить анимацию"
/>
) : (
<Pause
className={cn(
'h-4 w-4',
'text-slate-400/50 hover:text-accent',
'dark:text-rose-50/20 dark:hover:text-accent-dark',
)}
aria-label="Остановить анимацию"
/>
)}
</button>
</section>
);
}