Skip to content

Commit

Permalink
feat(my-recruit): sticky 애니메이션 구현 (#33)
Browse files Browse the repository at this point in the history
* feat(my-recruit): sticky 애니메이션 구현

* save
  • Loading branch information
qkrdmstlr3 authored Aug 24, 2024
1 parent 4c545a6 commit 66792dd
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 19 deletions.
46 changes: 35 additions & 11 deletions src/app/(sidebar)/my-recruit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,24 @@ import { NewRecruitDialogContent } from './components/NewRecruitDialogContent/Ne
import { RightSidebar } from './containers/RightSidebar/RightSidebar';
import { DndContextWithOverlay, DragEndEvent } from '@/lib/dnd-kit/dnd-kit';
import { InfoCard } from '@/components/InfoCard';
import { AnimatePresence } from 'framer-motion';
import { AnimatePresence, motion } from 'framer-motion';
import { usePostRecruit } from './api/usePostRecruit';
import { CardData } from './components/NewRecruitDialogContent/NewRecruitDialogContent';
import { cn } from '@/utils';
import { color } from '@/system/token/color';
import { usePostCardToRecruit } from './api/usePostCardToRecruit';
import { useScroll } from '@/hooks/useScroll';
import { If } from '@/system/utils/If';
import { fontSize } from '@/system/token/typography';

const STICKY_THRESHOLD = 30;

export default function MyRecruit() {
const [sidebarOpened, setSidebarOpened] = useState(false);

const headerRef = useRef<HTMLDivElement>(null);
const [isSticky, setIsSticky] = useState(false);
useScroll(headerRef.current, (y) => setIsSticky(y > 100));
useScroll(headerRef, (y) => setIsSticky(y > STICKY_THRESHOLD));

const { mutate: mutatePostCard } = usePostRecruit();
const { mutate: mutatePostCardToRecruit } = usePostCardToRecruit();
Expand All @@ -40,18 +44,29 @@ export default function MyRecruit() {
return (
<DndContextWithOverlay OverlayElement={InfoCard} onDragEnd={onDragEnd}>
<Dialog>
<div className="flex max-h-[100vh] overflow-auto">
<div ref={headerRef} className="flex max-h-[100vh] overflow-auto">
<div className="flex-1 max-w-[1700px] mx-auto">
<div className="sticky top-0 px-[80px] z-[100]">
<Spacing size={64} className="bg-neutral-1" />
<div className="flex justify-between bg-neutral-1">
<h1 className="text-title2 font-bold">내 공고</h1>
<Spacing size={STICKY_THRESHOLD} />
<div className="sticky top-0 z-[100] bg-neutral-1">
<Spacing size={34} />
<div className="flex justify-between items-center px-[80px] bg-neutral-1">
<motion.h1
variants={{
big: { fontSize: fontSize.title2 },
small: { fontSize: fontSize.heading1 },
}}
animate={isSticky ? 'small' : 'big'}
className="text-title2 font-bold">
내 공고
</motion.h1>
<div className="flex gap-[16px]">
<TouchButton
layout
disabled={sidebarOpened}
className="bg-white flex items-center gap-[4px] py-[8px] px-[12px] rounded-[6px] border-neutral-5 border-[1px]"
onClick={() => setSidebarOpened(!sidebarOpened)}>
<Icon name="copy" size={16} color={sidebarOpened ? color.neutral20 : color.neutral95} />

<span
className={
'text-label1 ' + cn('font-semibold', sidebarOpened ? 'text-neutral-20' : 'text-neutral-95')
Expand All @@ -61,15 +76,24 @@ export default function MyRecruit() {
</TouchButton>
<Dialog.Trigger asChild>
<div>
<TouchButton className="bg-neutral-95 flex items-center gap-[4px] py-[8px] px-[16px] rounded-[6px]">
<Icon name="add" size={24} color="#20E79D" />
<span className="text-label1 text-white font-semibold">새 공고</span>
<TouchButton layout>
<motion.div
initial={{ padding: '8px 16px' }}
variants={{ longPadding: { padding: '8px 16px' }, shortPadding: { padding: '8px 8px' } }}
animate={isSticky ? 'shortPadding' : 'longPadding'}
className="bg-neutral-95 flex items-center gap-[4px] rounded-[6px]">
<Icon name="add" size={24} color="#20E79D" />
{!isSticky && <span className="text-label1 text-white font-semibold">새 공고</span>}
</motion.div>
</TouchButton>
</div>
</Dialog.Trigger>
</div>
</div>
<Spacing size={34} className="bg-neutral-1" />
<Spacing size={34} />
<If condition={isSticky}>
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="bg-neutral-5 h-[1px]" />
</If>
</div>
<div className="px-[80px]">
<Spacing size={20} />
Expand Down
4 changes: 0 additions & 4 deletions src/container/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { Collapsible } from './Collapsible/Collapsible';
import { useRouter, usePathname } from 'next/navigation';
import { MY_RECRUIT_PATH, MY_INFO_PATH } from '@/route';

// FIXME:
const SELECTED = true;
const SIDEBAR_CLASSNAME = {
expanded: 'w-[220px]',
shrinked: 'w-[72px]',
Expand Down Expand Up @@ -38,9 +36,7 @@ export function Sidebar() {
<SidebarButton iconName="search" selected={false} expanded={expanded} expandedText="태그 검색" />
<SidebarButton iconName="bell" selected={false} expanded={expanded} expandedText="알림" />
{/* <SidebarButton iconName="memo" selected={false} expanded={expanded} expandedText="메모 모아보기" /> */}

<div className="w-full px-[16px] h-[1px] bg-[#37383C]" />

<Collapsible collapsed={expanded ? myInfoCollapsed : true} onCollapsedChange={setMyInfoCollapsed}>
<SidebarButton
iconName={pathname === MY_INFO_PATH ? 'profileFill' : 'profile'}
Expand Down
9 changes: 5 additions & 4 deletions src/hooks/useScroll.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useEffect } from 'react';
import { RefObject, useEffect } from 'react';

export function useScroll(target: HTMLDivElement | null, callback: (topOffsetY: number) => void) {
export function useScroll(target: RefObject<HTMLDivElement>, callback: (topOffsetY: number) => void) {
useEffect(() => {
const element = target ?? window;
const element = target.current ?? window;
console.log(element);

const callback2 = () => {
const topOffsetY = target?.offsetTop ?? window.scrollY;
const topOffsetY = target.current?.scrollTop ?? window.scrollY;
callback(topOffsetY);
};

Expand Down

0 comments on commit 66792dd

Please sign in to comment.