Skip to content

Commit

Permalink
[노철] - TooltipButton 구현 (#489)
Browse files Browse the repository at this point in the history
* ✨ : #488 - share 아이콘 추가

* ♻️ : #488 - 카카오버튼  null check 추가

* ✨ : #488 - TooltipButton 추가

* ♻️ : #488 - plan 페이지 TooltipButton 적용

* ♻️ : #488 - index export 적용

* ♻️ : #488 - 불필요한 함수 제거

* ♻️ : #488 - 컴포넌트 설명 주석 추가

* 💄 : #488 - trigger 배경색 변경

* ♻️ : #488 - optionsPosition props로 변경
  • Loading branch information
qkdl60 authored Mar 13, 2024
1 parent d7e79b4 commit 9919f2f
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/app/plans/[planId]/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
&--share {
margin: 1rem 0;
display: flex;
flex-direction: column;
gap: 0.5rem;

justify-content: space-between;
&--buttons {
display: flex;
gap: 1rem;
Expand Down
24 changes: 22 additions & 2 deletions src/app/plans/[planId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ModalBasic,
Popover,
ReadOnlyPlan,
TooltipButton,
} from '@/components';
import classNames from 'classnames';
import Link from 'next/link';
Expand Down Expand Up @@ -77,7 +78,26 @@ export default function PlanIdPage({ params }: { params: { planId: string } }) {
{isMyPlan && (
<div className="plans-page--share">
<h2>공유하기</h2>
<div className="plans-page--share--buttons">
<TooltipButton.Main optionsPosition="top">
<TooltipButton.Options>
<label className="font-size-xs" onClick={handleCopyLink}>
<Icon name="COPY" color="text-100" size="md" />
링크 복사
</label>
<label className="font-size-xs">
<KakaoShareButton linkURL={currentURL} />
카카오톡
</label>
</TooltipButton.Options>
<TooltipButton.Trigger>
<label>
<Icon name="SHARE" color="text-100" size="md" />
공유하기
</label>
</TooltipButton.Trigger>
</TooltipButton.Main>
{/*TODO 기존 공유버튼 삭제 예정 */}
{/* <div className="plans-page--share--buttons">
<label className="font-size-xs" onClick={handleCopyLink}>
<Icon name="COPY" color="text-100" size="md" />
링크 복사
Expand All @@ -86,7 +106,7 @@ export default function PlanIdPage({ params }: { params: { planId: string } }) {
<KakaoShareButton linkURL={currentURL} />
카카오톡
</label>
</div>
</div> */}
</div>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions src/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const ICON_NAME_MAP = {
COPY: 'content_copy',
ARROW_RIGHT: 'subdirectory_arrow_right',
CANCEL: 'cancel',
SHARE: 'share',
};

interface IconProps {
Expand Down
6 changes: 3 additions & 3 deletions src/components/KakaoShareButton/KakaoShareButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ export default function KakaoShareButton({ linkURL }: KakaoShareButtonProps) {
const kakao = (window as any).Kakao;

useEffect(() => {
kakao.cleanup();
kakao.init(process.env.NEXT_PUBLIC_KAKAO_JAVASCRIPT_KEY);
kakao?.cleanup();
kakao?.init(process.env.NEXT_PUBLIC_KAKAO_JAVASCRIPT_KEY);
return () => {
kakao.cleanup();
kakao?.cleanup();
};
}, [kakao]);

Expand Down
116 changes: 116 additions & 0 deletions src/components/TooltipButton/TooltipButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
'use client';

import { useModalClose } from '@/hooks';
import classNames from 'classnames';
import {
ReactElement,
createContext,
useContext,
useRef,
useState,
} from 'react';
import './index.scss';

//TooltipButton컴포넌트 내에서 공유할 상태, 함수
const contextDefaultValue = {
isOpen: false, // 툴팁 열림 여부
handleSetIsOpen: () => {}, //툴팁 상태 변경 함수
handleCloseTooltip: () => {}, //툴팁 닫기 함수
optionsPosition: 'top' as Position, //Options컴포넌트 위치(열림 방향을 위해서 필요)
};

type Position = 'top' | 'bottom';

const TooltipButtonContext = createContext<{
isOpen: boolean;
handleSetIsOpen: () => void;
handleCloseTooltip: () => void;
optionsPosition: Position;
}>(contextDefaultValue);

interface MainProps {
className?: string; // 위치조정이이나, css 스탕일링을 위한 className
children: ReactElement[];
optionsPosition: Position;
}

const Main = ({ children, className, optionsPosition }: MainProps) => {
const [isOpen, setIsOpen] = useState<boolean>(false);

const handleCloseTooltip = () => {
setIsOpen(false);
};
const handleSetIsOpen = () => {
setIsOpen(!isOpen);
};

return (
<div className={classNames('tooltip-button__main', className)}>
<TooltipButtonContext.Provider
value={{
handleCloseTooltip,
optionsPosition,
isOpen,
handleSetIsOpen,
}}>
{children}
</TooltipButtonContext.Provider>
</div>
);
};

interface TriggerProps {
children: ReactElement;
className?: string;
}
//열고 닫힘 trigger 역할을 할 컴푸넌트 children을 넣얼줄때 외부에서 직접 스타일링 가능, 그외 부분 클릭시 Tooltip 닫힘
const Trigger = ({ children, className }: TriggerProps) => {
const { handleSetIsOpen, handleCloseTooltip } =
useContext(TooltipButtonContext);
const triggerRef = useRef(null);
useModalClose(triggerRef, handleCloseTooltip);
return (
<button
ref={triggerRef}
onClick={handleSetIsOpen}
className={classNames(className, 'tooltip-button__trigger')}>
{children}
</button>
);
};

interface OptionsProps {
className?: string;
children: ReactElement[] | ReactElement;
}
//열렸을때 보여줄 아이템들
const Options = ({ className, children }: OptionsProps) => {
const { isOpen, optionsPosition } = useContext(TooltipButtonContext);
return (
<ul
className={classNames(
className,
'tooltip-button__list',
`position-${optionsPosition}`,
isOpen ? 'open' : 'close',
)}>
{Array.isArray(children) ? (
children.map((component, index) => (
<li
data-order={index + 1}
className={classNames(
'tooltip-button__list__item',
isOpen ? 'open' : 'close',
)}
key={index}>
{component}
</li>
))
) : (
<li className="tooltip-button__list__item ">{children}</li>
)}
</ul>
);
};

export { Main, Options, Trigger };
67 changes: 67 additions & 0 deletions src/components/TooltipButton/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.tooltip-button {
&__main {
overflow: visible;
position: relative;
}

&__trigger {
background-color: transparent;
}

&__list {
display: flex;
flex-direction: column;
background-color: transparent;
gap: 0.75rem;
position: absolute;
transition: all 0.3s;
text-wrap: nowrap;

&__item {
background-color: transparent;
transition: all 0.2s;
}
&.position-top {
bottom: 140%;
}

&.position-top &__item {
&.close {
transform: translateY(100%);
opacity: 0;
visibility: hidden;
}
&.open {
transform: translateY(0);
opacity: 1;
}
}

&.position-bottom {
top: 125%;
}

&.position-bottom &__item {
&.close {
transform: translateY(-100%);
opacity: 0;
visibility: hidden;
}
&.open {
transform: translateY(0);
opacity: 1;
}
}
//순차적으로 나타나고 사라지는 애니메이션을 위한 delay
$n: 5;

@for $i from 1 through $n {
&__item:nth-child(#{$i}) {
transition-delay: calc(0.1s * $i);
}
}
}
&__list.close {
visibility: hidden;
}
}
1 change: 1 addition & 0 deletions src/components/TooltipButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as TooltipButton from '@components/TooltipButton/TooltipButton';
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ export { default as ToTopFloatingButton } from '@/components/ToTopFloatingButton
export { default as WritableRemindItem } from '@components/RemindItem/WritableRemindItem/WritableRemindItem';
export { default as ReadOnlyPlan } from '@components/ReadOnlyPlan/ReadOnlyPlan';
export { default as WrongApproach } from '@components/WrongApproach/WrongApproach';
export { TooltipButton } from '@components/TooltipButton/index';
export { Popover } from '@components/Popover';
3 changes: 2 additions & 1 deletion src/types/IconName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ export type IconName =
| 'COPY'
| 'HELP'
| 'ARROW_RIGHT'
| 'CANCEL';
| 'CANCEL'
| 'SHARE';

0 comments on commit 9919f2f

Please sign in to comment.