radix와 shadcn
radix-ui는 스타일없이 제공되는 UI 컴포넌트 라이브러리로 MUI에 비해 유연성이 높다.
shadcn은 radix UI및 tailwind css를 사용해 구축된 재사용 가능한 컴포넌트로 버튼 같이 디자인이 다양하게 사용되는 컴포넌트에 사용하면 좋다.
radix이용해서 progress bar 컴포넌트 만들기
일단 radix에서 progress 컴포넌트를 설치해준 후 tailwind css를 적용해주었다.
css -> tailwind로 바꿔주는 사이트를 이용(https://www.loopple.com/tools/css-to-tailwind-converter)
이 progress bar를 사용하는 페이지는 두 종류인데,
하나는 멀티페이지 방식의 resume 사이트로 다음페이지로 넘어갈 때마다 progress가 올라가는 방식이고
하나는 무한스크롤 페이지다.
'use client'
import React from 'react'
import * as Progress from '@radix-ui/react-progress'
function progressBar() {
const [progress, setProgress] = React.useState(0)
React.useEffect(() => {
const timer = setTimeout(() => setProgress(30), 100)
return () => clearTimeout(timer)
}, [])
return (
<Progress.Root className="relative w-full h-2 overflow-hidden bg-opacity-50 rounded-sm bg-base-primary-light" value={progress}>
<Progress.Indicator className="w-full h-full bg-brand-primary-light [transition:transform_660ms_cubic-bezier(0.65,_0,_0.35,_1)]" style={{ transform: `translateX(-${100 - progress}%)` }} />
</Progress.Root>
)
}
export default progressBar
두 곳에서 재사용가능하게 만드려면 어떻게 해야 할 지 잘 모르겠지만 일단 멀티페이지 방식에서는
props로 이전페이지까지의 진행도, 현재 페이지에서 진행도를 넘겨주면 될 것 같았다.
이전 페이지까지의 진행도는 progress useState의 초기값으로 주면되고,
현재 페이지 진행도는 useEffect 안에서 setProgress 값의 인자로 넘겨주면 된다.
useEffect 안에서 return()=> clearTimeout(timer)
가 있는 이유는
만약 사용자가 페이지를 스킵스킵했을 경우, 이전에 동작을 마치지 않은 timer를 clear시키기 위한 cleanup함수 같다.
스크롤화면에서 프로그레스 바 구현 , tailwind 동적클래스 할당
멀티페이지 방식에서 프로그래스바를 스크롤 방식에서도 재사용하고 싶었으나
동작방식이 다르다고 판단해서 따로 만들어주었다.
function page() {
const [scrollPercentage, setScrollPercentage] = useState(0)
useEffect(() => {
const handleScroll = () => {
const windowHeight = window.innerHeight
const documentHeight = document.documentElement.scrollHeight
const scrollY = window.scrollY
const scrollPercent = scrollY / (documentHeight - windowHeight)
console.log(scrollPercent)
setScrollPercentage(scrollPercent)
}
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
})
const progressBarStyle = {
width: `${scrollPercentage * 100}%`,
};
return (
<>
<article>
<div className="fixed top-0 left-0 right-0 w-full h-2 overflow-hidden rounded-sm bg-base-secondary-light">
<div className="h-full bg-brand-primary-light [transition:transform_660ms_cubic-bezier(0.65,_0,_0.35,_1)]" style={progressBarStyle}></div>
</div>
{questions.map((question, index) => {
return (
<div className="pb-8" key={index}>
<div className="w-full pb-2 label-semi">{question}</div>
<RadioGroup.Root className="flex justify-center gap-4 py-2">
{answers.map((answer) => {
return (
<div className="flex flex-col items-center gap-2 ">
<RadioGroup.Item
className="flex justify-center items-center bg-[white] w-[20px] h-[20px] rounded-[100%] [box-shadow:0_2px_10px_var(--black-a7)]
outline outline-base-secondary-light focus:[box-shadow:0_0_0_2px_black]
"
value={answer.label}
>
<RadioGroup.Indicator className="after:content-[''] after:block rounded-full relative w-[75%] h-[75%] bg-brand-primary-normal "></RadioGroup.Indicator>
</RadioGroup.Item>
<label className="btn-semi">{answer.score}</label>
</div>
)
})}
</RadioGroup.Root>
</div>
)
})}
</article>
</>
)
}
export default page
window.innerHeight
: 브라우저의 뷰포트 높이document.documentElement.scrollHeight
: 문서의 실제 높이를 나타냄window.scrollY
: 현재 스크롤 위치를 저장(scrollY / (documentHeight - windowHeight))
: 스크롤 가능한 전체 내용의 크기- 현재 브라우저 뷰포트 즉, 사용자가 스크롤 할 수 있는 전체 영역의 크기로 scrollY를 나눠준다.= 스크롤 가능한 전체 영역에서 현재 스크롤 위치는 얼만큼 차지하고 있는지 계산
const progressBarStyle = {
width: `${scrollPercentage * 100}%`,
};
이 코드를 따로 빼지 않고 tailwind에 동적으로 스타일을 주도록 코드를 작성하고 싶었다.
였으나..부분적으로 감싸는건 공식문서에서 권장하고 있지않고 전체적으로 {}
이렇게 감싸줘야 한다.
그래서 그냥 style로 주기로 했다
'TIL' 카테고리의 다른 글
리액트 다루는 기술 1주차 스터디 정리 (0) | 2024.01.16 |
---|---|
[기업플젝 기록] 리액트 - useContext로 모달 관리하기 (0) | 2023.12.14 |
[TIL] 12/5 - 리액트, 기업 프로젝트 기록 (1) | 2023.12.05 |
SQL 고득점 Kit 풀이- SELCT, STRING& DATE (1) (0) | 2023.10.20 |