-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
…gButton [FE][FEAT] #145 - 플로팅 버튼 구현
- Loading branch information
Showing
9 changed files
with
213 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
frontend/src/component/common/FloatingButton/FloatingButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import classNames from 'classnames'; | ||
import React, { useState } from 'react'; | ||
import { ButtonState } from '../enums'; | ||
import { IconType, ToolCategory } from '../types'; | ||
|
||
/** | ||
* FloatingButton 컴포넌트는 도구 선택 및 메뉴 토글 기능을 제공하는 플로팅 버튼을 렌더링합니다. | ||
* @remarks | ||
* 메뉴가 열리면 툴 선택과 설명 표시가 가능하며, 메뉴를 닫을 때 버튼 아이콘이 변경됩니다. | ||
* @example | ||
* return ( | ||
* <FloatingButton /> | ||
* ) | ||
*/ | ||
export const FloatingButton = () => { | ||
const [toolType, setToolType] = useState<ButtonState>(ButtonState.CLOSE); | ||
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false); | ||
|
||
/** | ||
* @remarks | ||
* - 메뉴를 토글하는 함수입니다. | ||
*/ | ||
const toggleMenu = () => { | ||
setIsMenuOpen(prev => !prev); | ||
if (!isMenuOpen) { | ||
setToolType(ButtonState.OPEN); | ||
} else { | ||
setToolType(ButtonState.CLOSE); | ||
} | ||
}; | ||
|
||
/** | ||
* @remarks | ||
* 툴을 선택하고 메뉴를 닫는 함수입니다. | ||
* @param {ButtonState} type - 선택된 툴 타입 | ||
*/ | ||
const handleMenuClick = (type: ButtonState) => { | ||
setToolType(type); | ||
setIsMenuOpen(!isMenuOpen); | ||
}; | ||
|
||
return ( | ||
<div | ||
className={classNames('absolute', 'bottom-5', 'right-10', 'flex', 'flex-col', 'items-center')} | ||
> | ||
<button | ||
type="button" | ||
onClick={toggleMenu} | ||
className={classNames( | ||
'absolute', | ||
'bottom-0', | ||
'bg-blueGray-800', | ||
'w-12', | ||
'h-12', | ||
'rounded-full', | ||
'flex', | ||
'justify-center', | ||
'items-center', | ||
'text-white', | ||
'shadow-floatMenuButton', | ||
'z-10', | ||
)} | ||
> | ||
{React.createElement(IconType[toolType], { className: 'w-6 h-6' })} | ||
</button> | ||
|
||
{ToolCategory.map(({ type, description, icon }, index) => ( | ||
<button | ||
type="button" | ||
onClick={() => handleMenuClick(type)} | ||
key={type} | ||
className={classNames( | ||
'w-10', | ||
'h-10', | ||
'bg-blueGray-200', | ||
'text-white', | ||
'rounded-full', | ||
'flex', | ||
'items-center', | ||
'justify-center', | ||
'absolute', | ||
'transition-all', | ||
'duration-300', | ||
'shadow-floatButton', | ||
{ | ||
'shadow-none': !isMenuOpen, | ||
}, | ||
)} | ||
style={{ bottom: toolType === ButtonState.OPEN ? `${48 * index + 64}px` : '0px' }} | ||
> | ||
<div className={classNames('flex', 'items-center')}> | ||
{React.createElement(icon, { className: 'w-5 h-5' })} | ||
{isMenuOpen && ( | ||
<div | ||
className={classNames( | ||
'w-20', | ||
'h-8', | ||
'bg-blueGray-200', | ||
'absolute', | ||
'right-12', | ||
'text-white', | ||
'text-xs', | ||
'flex', | ||
'items-center', | ||
'justify-center', | ||
'rounded-md', | ||
'opacity-[0.5]', | ||
)} | ||
> | ||
{description} | ||
</div> | ||
)} | ||
</div> | ||
</button> | ||
))} | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/** | ||
* @remarks | ||
* ButtonState Enum은 버튼이 가질 수 있는 모든 종류를 정의합니다. | ||
* @example | ||
* const buttonState = ButtonState.OPEN; | ||
*/ | ||
export enum ButtonState { | ||
CLOSE = 'CLOSE', // 메뉴 닫기 버튼 | ||
OPEN = 'OPEN', // 메뉴 열기 버튼 | ||
START_MARKER = 'START_MARKER', // 출발지 설정 버튼 | ||
DESTINATION_MARKER = 'DESTINATION_MARKER', // 도착지 설정 버튼 | ||
LINE_DRAWING = 'LINE_DRAWING', // 경로 그리기 버튼 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { FaPaintBrush } from 'react-icons/fa'; | ||
import { HiLocationMarker, HiOutlineDotsHorizontal, HiOutlineLocationMarker } from 'react-icons/hi'; | ||
import { SlLayers } from 'react-icons/sl'; | ||
import { ButtonState } from './enums'; | ||
|
||
/** | ||
* IconType 객체는 각 버튼 타입에 해당하는 아이콘 컴포넌트를 매핑합니다. | ||
* @type {Record<ButtonState, React.ComponentType>} | ||
*/ | ||
export const IconType = { | ||
CLOSE: SlLayers, // 닫기 버튼 아이콘 | ||
OPEN: HiOutlineDotsHorizontal, // 열기 버튼 아이콘 | ||
START_MARKER: HiLocationMarker, // 출발지 설정 아이콘 | ||
DESTINATION_MARKER: HiOutlineLocationMarker, // 도착지 설정 아이콘 | ||
LINE_DRAWING: FaPaintBrush, // 경로 그리기 아이콘 | ||
}; | ||
/** | ||
* @remarks | ||
* ToolCategory 배열은 각 저작도구에 대한 정보를 담고 있습니다. | ||
* 각 툴은 버튼 타입, 설명, 아이콘을 포함합니다. | ||
* 이 배열은 버튼의 목록을 정의하고, 이후 툴을 확장할 때 유용합니다. | ||
* 또한, OPEN과 CLOSE 등 저작 도구가 아닌 종류는 제외합니다. | ||
* @example | ||
* const tool = ToolCategory[0]; | ||
*/ | ||
export const ToolCategory = [ | ||
{ | ||
type: ButtonState.LINE_DRAWING, // 경로 그리기 툴 | ||
description: '경로 그리기', // 툴 설명 | ||
icon: IconType.LINE_DRAWING, // 툴 아이콘 | ||
}, | ||
{ | ||
type: ButtonState.START_MARKER, // 출발지 설정 툴 | ||
description: '출발지 설정', // 툴 설명 | ||
icon: IconType.START_MARKER, // 툴 아이콘 | ||
}, | ||
{ | ||
type: ButtonState.DESTINATION_MARKER, // 도착지 설정 툴 | ||
description: '도착지 설정', // 툴 설명 | ||
icon: IconType.DESTINATION_MARKER, // 툴 아이콘 | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.