-
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
* ✨ feat: css 카테고리 타입 정의 * ✨ feat: css 카테고리를 정의하는 객체 추가 * ✨ feat: css 카테고리 속성 편집창 정적 ui 구현 * 🙀 chore: 파일 구조 변경에 따른 import문 변경 * 🔥 remove: 파일 이동에 따른 파일 삭제 * ✨ feat: dev 브랜치에서 pull 후 변경사항 적용 및 툴팁 구현 (미완성) * ✨ feat: css 툴팁 컴포넌트 추가 (미완성) * ✨ feat: 마우스 좌표에 따른 css 툴팁 위치 계산 로직 추가 (미완성) * ✨ feat: css 툴팁 컴포넌트 구현 * ✨ feat: debounce 메소드 구현 * ✨ feat: 브라우저 resize 이벤트 핸들링 후 innerWidth, innerHeight를 리턴하는 커스텀훅 추가 * ✨ feat: css 속성 편집 관련 커스텀 훅 제작 * ✨ feat: css 속성 툴팁 렌더링 위치 관련 로직을 제공하는 커스텀 훅 제공 * 🙀 chore: index.ts re-export 추가 * 🎨 style: input type=color 스타일 조정 * 🚚 rename: cssCategoryList 소스파일 shared/util 폴더로 이동 * 🚚 rename: tooltip 관련 커스텀 훅 소스파일 이름 변경 * 🙀 chore: useRef를 사용하는 부분 삭제 * 🔨 refactor: CssPropsSelectBox에서 header 컴포넌트 분리 * 🔨 refactor: CssPropsSelectBox에서 카테고리를 선택하는 부분 CategoryBar 컴포넌트로 분리 * 🔨 refactor: 카테고리 선택 버튼 컴포넌트로 분리 * 🔨 refactor: CssPropsSelectBox에서 CSS 옵션을 부여하는 부분 분리 * 🔨 refactor: CSS 옵션을 부여하는 컴포넌트 리스트에서 재사용 컴포넌트 분리 * ✨ feat: css 옵션 부여 관련 기능 커스텀훅 제작 * ✨ feat: Css 속성 설정 관련 상태 변수들을 전역변수화 시킴 * ✨ feat: tootlip 좌표 관련 상태 변수들을 전역변수화 시킴 * 🔥 remove: 불필요한 파일 삭제 * 🔨 refactor: CssPropsSelectBox 컴포넌트 분리 완료 * 🙀 chore: import문 변경 * 🙀 chore: 새롭게 추가된 컴포넌트 및 커스텀 훅 index.ts를 통해 re-export * ✨ feat: 여러 개의 클래스에 css 옵션을 적용하도록 상태 변수 자료 구조 변경 * 🙀 chore: key 속성 부여 * ✨ feat: class명이 빈 문자열이거나 클래스를 선택해주세요 일 시 속성 편집이 불가능하게 변경 * 🙀 chore: isChecked, cssOption 상태변수로 전역 상태 변수값을 조정하도록 변경 * ✨ feat: css 속성의 체크 여부 isChecked, css 속성에 부여된 값 cssOption 상태변수 추가 및 초기화 로직 추가 * ✨ feat: 워크스페이스 페이지 렌더링 시 전역 상태 초기화 진행 * ✨ feat: css 코드 생성 메소드 작성 * 🙀 chore: 변환하기 버튼에 css 코드 생성 메소드 추가 * 🐛 fix: selected로 값 조작 시 react로 DOM 조작 시 문제가 발생할 수 있는 경고 해결 * 🐛 fix: class이름이 빈 문자열일 때도 css코드가 생성되던 문제 해결 * 🙀 chore: 불필요한 코드 삭제 * 🐛 fix: 빌드 오류 수종
- Loading branch information
1 parent
561cc66
commit 3d5028e
Showing
27 changed files
with
781 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { TcssCategory, TcssCategoryItem } from '@/shared/types'; | ||
|
||
import { useCssPropsStore } from '@/shared/store'; | ||
|
||
type CssCategoryButtonProps = { | ||
cssCategory: { | ||
category: TcssCategory; | ||
items: TcssCategoryItem[]; | ||
}; | ||
}; | ||
|
||
export const CssCategoryButton = ({ cssCategory }: CssCategoryButtonProps) => { | ||
const { selectedCssCategory, setSelectedCssCategory } = useCssPropsStore(); | ||
return ( | ||
<button | ||
key={cssCategory.category} | ||
onClick={() => setSelectedCssCategory(cssCategory.category)} | ||
className={`text-bold-sm flex cursor-pointer rounded px-3 py-2.5 text-gray-200 ${selectedCssCategory === cssCategory.category && 'text-gray-black bg-yellow-500'}`} | ||
> | ||
{cssCategory.category} | ||
</button> | ||
); | ||
}; |
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,115 @@ | ||
import { useCssOptionItem, useCssOptions, useCssTooltip } from '@/shared/hooks'; | ||
|
||
import { CssTooltip } from '@/entities'; | ||
import Question from '@/shared/assets/question.svg?react'; | ||
import { TcssCategoryItem } from '@/shared/types'; | ||
import { useCssPropsStore } from '@/shared/store'; | ||
|
||
type CssOptionItemProps = { | ||
cssItem: TcssCategoryItem; | ||
index: number; | ||
}; | ||
|
||
export const CssOptionItem = ({ cssItem, index }: CssOptionItemProps) => { | ||
const { totalCssPropertyObj, currentCssClassName } = useCssPropsStore(); | ||
const { handleCssPropertyCheckboxChange, handleCssOptionChange, handleColorChange } = | ||
useCssOptions(); | ||
|
||
const { | ||
cssOptionValue, | ||
isHover, | ||
indexOfHover, | ||
isChecked, | ||
cssOption, | ||
handleMouseEnter, | ||
handleEnterKey, | ||
handleMouseLeave, | ||
handleChangeInputValue, | ||
} = useCssOptionItem(cssItem); | ||
|
||
const { leftX, topY } = useCssTooltip(); | ||
|
||
return ( | ||
<div | ||
className={`flex h-[66px] w-full flex-shrink-0 items-center justify-between rounded-lg px-4 ${ | ||
totalCssPropertyObj[currentCssClassName] && | ||
totalCssPropertyObj[currentCssClassName].checkedCssPropertyObj[cssItem.label] | ||
? 'bg-yellow-500' | ||
: 'bg-gray-50' | ||
} `} | ||
> | ||
<div className="flex items-center gap-5"> | ||
<input | ||
type="checkbox" | ||
checked={isChecked} | ||
onChange={() => handleCssPropertyCheckboxChange(cssItem.label, isChecked, cssOption)} | ||
title={cssItem.label} | ||
className="h-5 w-5 appearance-none rounded border border-gray-100 bg-center bg-no-repeat checked:bg-white checked:bg-[url('@/shared/assets/check.svg')]" | ||
disabled={ | ||
currentCssClassName.length === 0 || currentCssClassName === '클래스를 선택해주세요' | ||
} | ||
/> | ||
<div className="flex items-center gap-2"> | ||
<p className="text-semibold-md text-gray-black max-w-36 border-gray-100"> | ||
{cssItem.label} | ||
</p> | ||
<Question | ||
onMouseEnter={(e) => handleMouseEnter(e, index)} | ||
onMouseLeave={handleMouseLeave} | ||
/> | ||
<CssTooltip | ||
description={cssItem.description} | ||
isOpen={isHover && indexOfHover === index} | ||
leftX={leftX} | ||
topY={topY} | ||
/> | ||
</div> | ||
</div> | ||
{cssItem.type === 'select' && ( | ||
<select | ||
id={cssItem.label} | ||
className="bg-gray-white focus:ring-gray-black text-semibold-md focus:border-gray-black w-[120px] truncate rounded-lg border border-gray-100 px-2 py-1 outline-none" | ||
onChange={(e) => handleCssOptionChange(cssItem.label, e.target.value)} | ||
value={cssOption} | ||
disabled={ | ||
currentCssClassName.length === 0 || currentCssClassName === '클래스를 선택해주세요' | ||
} | ||
> | ||
{cssItem.option?.map((option) => ( | ||
<option id={option} value={option} key={option}> | ||
{option} | ||
</option> | ||
))} | ||
</select> | ||
)} | ||
{cssItem.type === 'input' && ( | ||
<input | ||
type="text" | ||
className="text-semibold-md focus:border-gray-black placeholder:text-semibold-sm w-28 rounded-lg border border-gray-100 px-2 py-1 placeholder-gray-100 focus:border focus:outline-none" | ||
placeholder="값을 입력하세요" | ||
onBlur={(e) => handleCssOptionChange(cssItem.label, e.target.value)} | ||
onKeyDown={(e) => handleEnterKey(cssItem.label, e)} | ||
value={cssOptionValue} | ||
onChange={handleChangeInputValue} | ||
disabled={ | ||
currentCssClassName.length === 0 || currentCssClassName === '클래스를 선택해주세요' | ||
} | ||
/> | ||
)} | ||
{cssItem.type === 'color' && ( | ||
<div className="flex items-center gap-4"> | ||
<p>{cssOption}</p> | ||
<input | ||
type="color" | ||
onChange={(e) => handleColorChange(cssItem.label, e.target.value)} | ||
value={cssOption} | ||
className="h-5 w-5 cursor-pointer appearance-none bg-transparent" | ||
disabled={ | ||
currentCssClassName.length === 0 || currentCssClassName === '클래스를 선택해주세요' | ||
} | ||
/> | ||
</div> | ||
)} | ||
</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,23 @@ | ||
import { createPortal } from 'react-dom'; | ||
|
||
type CssTooltipProps = { | ||
description: string; | ||
isOpen: boolean; | ||
leftX: number; | ||
topY: number; | ||
}; | ||
|
||
export const CssTooltip = ({ description, isOpen, leftX, topY }: CssTooltipProps) => { | ||
if (!isOpen) { | ||
return null; | ||
} | ||
return createPortal( | ||
<div | ||
className={`text-gray-white fixed left-0 top-0 rounded-3xl ${topY >= 0 ? 'rounded-tl-none' : 'rounded-bl-none'} bg-green-500 px-3 py-2`} | ||
style={{ left: `${leftX + 18}px`, top: topY >= 0 ? `${topY + 8}px` : `${-topY}px` }} | ||
> | ||
<p>{description}</p> | ||
</div>, | ||
document.body | ||
); | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { useCssPropsStore, useCssTooltipStore } from '@/shared/store'; | ||
import { useEffect, useState } from 'react'; | ||
|
||
import { TcssCategoryItem } from '@/shared/types'; | ||
import { useCssOptions } from '@/shared/hooks'; | ||
|
||
export const useCssOptionItem = (cssItem: TcssCategoryItem) => { | ||
const { handleCssOptionChange } = useCssOptions(); | ||
const { setOffsetX, setOffsetY } = useCssTooltipStore(); | ||
const { currentCssClassName, totalCssPropertyObj } = useCssPropsStore(); | ||
|
||
const [cssOptionValue, setCssOptionValue] = useState<string>(''); | ||
const [isHover, setIsHover] = useState<boolean>(false); | ||
const [indexOfHover, setIndexOfHover] = useState<number>(-1); | ||
|
||
const [isChecked, setIsChecked] = useState<boolean>(false); | ||
const [cssOption, setCssOption] = useState<string>(''); | ||
|
||
useEffect(() => { | ||
if (totalCssPropertyObj[currentCssClassName]) { | ||
setCssOptionValue(totalCssPropertyObj[currentCssClassName].cssOptionObj[cssItem.label] || ''); | ||
} | ||
}, [currentCssClassName, totalCssPropertyObj, cssItem.label]); | ||
|
||
useEffect(() => { | ||
if (!totalCssPropertyObj[currentCssClassName]) { | ||
setIsChecked(false); | ||
setCssOption( | ||
cssItem.type === 'select' ? cssItem.option![0] : cssItem.type === 'color' ? '#000000' : '' | ||
); | ||
return; | ||
} | ||
setIsChecked( | ||
totalCssPropertyObj[currentCssClassName].checkedCssPropertyObj[cssItem.label] ?? false | ||
); | ||
if (!totalCssPropertyObj[currentCssClassName].cssOptionObj[cssItem.label]) { | ||
setCssOption(cssItem.type === 'select' ? cssItem.option![0] : ''); | ||
return; | ||
} | ||
setCssOption(totalCssPropertyObj[currentCssClassName].cssOptionObj[cssItem.label]); | ||
}, [totalCssPropertyObj, currentCssClassName]); | ||
|
||
/** | ||
* @description 엔터키 입력시 스타일 프로퍼티 변경 이벤트 핸들러 | ||
*/ | ||
const handleEnterKey = (property: string, e: React.KeyboardEvent<HTMLInputElement>) => { | ||
if (e.key === 'Enter') { | ||
handleCssOptionChange(property, e.currentTarget.value); | ||
e.currentTarget.blur(); | ||
e.preventDefault(); | ||
} | ||
}; | ||
|
||
/** | ||
* @description 마우스 엔터 이벤드 핸들러 | ||
*/ | ||
const handleMouseEnter = (e: React.MouseEvent<SVGElement, MouseEvent>, index: number) => { | ||
setIsHover(true); | ||
setIndexOfHover(index); | ||
setOffsetX(e.currentTarget.getBoundingClientRect().x); | ||
setOffsetY(e.currentTarget.getBoundingClientRect().y); | ||
}; | ||
|
||
const handleMouseLeave = () => { | ||
setIsHover(false); | ||
setIndexOfHover(-1); | ||
}; | ||
|
||
const handleChangeInputValue = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
setCssOptionValue(e.target.value); | ||
}; | ||
|
||
return { | ||
cssOptionValue, | ||
isHover, | ||
indexOfHover, | ||
isChecked, | ||
cssOption, | ||
handleEnterKey, | ||
handleMouseEnter, | ||
handleMouseLeave, | ||
handleChangeInputValue, | ||
}; | ||
}; |
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,35 @@ | ||
import { debounce } from '@/shared/utils'; | ||
import { useCallback } from 'react'; | ||
import { useCssPropsStore } from '@/shared/store'; | ||
|
||
export const useCssOptions = () => { | ||
const { setCheckedCssPropertyObj, setCssOptionObj, currentCssClassName } = useCssPropsStore(); | ||
|
||
const handleCssPropertyCheckboxChange = ( | ||
property: string, | ||
isChecked: boolean, | ||
cssOption: string | ||
) => { | ||
setCheckedCssPropertyObj(currentCssClassName, property, !isChecked); | ||
if (!isChecked) { | ||
setCssOptionObj(currentCssClassName, property, cssOption); | ||
} | ||
}; | ||
|
||
const handleCssOptionChange = (property: string, value: string) => { | ||
setCssOptionObj(currentCssClassName, property, value); | ||
}; | ||
|
||
const handleColorChange = useCallback( | ||
debounce((property: string, value: string) => { | ||
handleCssOptionChange(property, value); | ||
}, 200), | ||
[handleCssOptionChange] | ||
); | ||
|
||
return { | ||
handleCssPropertyCheckboxChange, | ||
handleCssOptionChange, | ||
handleColorChange, | ||
}; | ||
}; |
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,21 @@ | ||
import { useCssTooltipStore } from '@/shared/store'; | ||
import { useEffect } from 'react'; | ||
import { useWindowSize } from '@/shared/hooks'; | ||
|
||
export const useCssTooltip = () => { | ||
const { leftX, topY, offsetX, offsetY, setLeftX, setTopY } = useCssTooltipStore(); | ||
|
||
const { screenWidth, screenHeight } = useWindowSize(); | ||
|
||
useEffect(() => { | ||
const tooltipHeight = 40; | ||
setLeftX(offsetX); | ||
if (offsetY + tooltipHeight > screenHeight) { | ||
setTopY(-offsetY + tooltipHeight); // 높이를 벗어나는 것임 | ||
} else { | ||
setTopY(offsetY); | ||
} | ||
}, [offsetX, offsetY, screenWidth, screenHeight]); | ||
|
||
return { leftX, topY }; | ||
}; |
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,22 @@ | ||
import { useEffect, useState } from 'react'; | ||
|
||
import { debounce } from '@/shared/utils'; | ||
|
||
export const useWindowSize = () => { | ||
const [screenWidth, setScreenWidth] = useState<number>(window.innerWidth); | ||
const [screenHeight, setScreenHeight] = useState<number>(window.innerHeight); | ||
|
||
useEffect(() => { | ||
const handleResize = debounce(() => { | ||
setScreenWidth(window.innerWidth); | ||
setScreenHeight(window.innerHeight); | ||
}, 200); | ||
|
||
window.addEventListener('resize', handleResize); | ||
return () => { | ||
window.removeEventListener('resize', handleResize); | ||
}; | ||
}, []); | ||
|
||
return { screenWidth, setScreenWidth, screenHeight, setScreenHeight }; | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
export { useLoadingStore } from './useLoadingStore'; | ||
export { useModalStore } from './useModalStore'; | ||
export { useWorkspaceStore } from './useWorkspaceStore'; | ||
export { useCssPropsStore } from './useCssPropsStore'; | ||
export { useCssTooltipStore } from './useCssTooptipStore'; | ||
export { useClassBlockStore } from './useClassBlockStore'; |
Oops, something went wrong.