Skip to content

Commit 22eed92

Browse files
committed
fix: optimize stack image css rendering
1 parent 1864581 commit 22eed92

File tree

11 files changed

+108
-90
lines changed

11 files changed

+108
-90
lines changed

src/components/StackImage.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ function StackImage({stack, hasTooltip=true}: IStackImage) {
1313
const normalizedStack = stack.tagName.toLowerCase().replace(/\./g, '');
1414
const stackUrl = getStackUrl(stack);
1515

16-
const [isHover, setIsHover] = useState<boolean>(false);
1716
const [url, setUrl] = useState<string>(
1817
stackUrl != null ? stackUrl :
1918
`https://cdn.jsdelivr.net/gh/devicons/devicon/icons/${normalizedStack}/${normalizedStack}-original.svg`);
@@ -25,15 +24,13 @@ function StackImage({stack, hasTooltip=true}: IStackImage) {
2524
}
2625

2726
return (
28-
<div className='stack_layout'
29-
onClick={e => e.stopPropagation()}>
27+
<div className='stack_layout'>
3028
<img src={url}
3129
alt={stack.tagName}
3230
onError={loadOtherImage}
33-
onMouseEnter={() => setIsHover(true)}
34-
onMouseLeave={() => setIsHover(false)}/>
31+
onClick={e => e.stopPropagation()} />
3532
{ hasTooltip && (
36-
<span className={isHover ? 'visible' : ''}>{stack.tagName}</span>
33+
<span>{stack.tagName}</span>
3734
)}
3835
</div>
3936
);

src/components/cards/JobPostingCard.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ function JobPostingCard({id, title, companyName, jobPosition, jobType, imgUrl, d
2929
</div>
3030
</div>
3131
<div className='user_tag_layout'>
32-
<h5>직무</h5>
32+
<h4>직무</h4>
3333
<p>{JobPositionRecord[jobPosition]}</p>
3434
</div>
3535
<div className='user_tag_layout'>
36-
<h5>연차</h5>
36+
<h4>연차</h4>
3737
<p>{JobTypeRecord[jobType]}</p>
3838
</div>
3939
<div className='user_tag_layout'>
40-
<h5>마감일</h5>
40+
<h4>마감일</h4>
4141
<p>{time2Date(deadLine)}</p>
4242
</div>
4343

src/components/cards/MemberCard.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function MemberCard({userID, profileImageURL, memberLevel, nickname, position, s
4444
Api.fetch2Json(`/api/v1/likes/check/${userID}`)
4545
.then(res => setIsLiked(res.check))
4646
.catch(() => {});
47-
}, []);
47+
}, [myID, userID]);
4848

4949
function clickLike(e: React.MouseEvent) {
5050
e.stopPropagation();
@@ -139,11 +139,11 @@ function MemberCard({userID, profileImageURL, memberLevel, nickname, position, s
139139
</div>
140140

141141
<div className='user_tag_layout'>
142-
<h5>직무</h5>
142+
<h4>직무</h4>
143143
<p>{position.positionName}</p>
144144
</div>
145145
<div className='user_tag_layout'>
146-
<h5>온도</h5>
146+
<h4>온도</h4>
147147
<p>{score.toFixed(1)} ºC</p>
148148
</div>
149149
</div>

src/components/cards/MentorCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,12 @@ function MentorCard({thumbnailUrl, mentoringId, title, roleType, career, likes,
9393
</div>
9494

9595
<div className='mentor_tag_layout'>
96-
<h5>직무</h5>
96+
<h4>직무</h4>
9797
<p>{getTechListKor(roleType)}</p>
9898
</div>
9999

100100
<div className='mentor_tag_layout'>
101-
<h5>경력</h5>
101+
<h4>경력</h4>
102102
<p>{career}</p>
103103
</div>
104104

src/components/dialogLayout/MentorDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ function MentorDialog({mentoringId, isOpen, hideMentorCard}: IMentorDialog) {
167167

168168
<h3>{mentoringInfo.title}</h3>
169169
<div className='user_detail_info_layout'>
170-
<h5>직무</h5>
170+
<h4>직무</h4>
171171
<span>{getTechListKor(mentoringInfo.roleType)}</span>
172172
</div>
173173
<div className='user_detail_info_layout'>
174-
<h5>경력</h5>
174+
<h4>경력</h4>
175175
<span>{mentoringInfo.career}</span>
176176
</div>
177177

src/components/inputs/TechStackSelector.tsx

Lines changed: 76 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useEffect, useMemo, useRef, useState} from 'react';
1+
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
22
import StackImage from '@components/StackImage.tsx';
33
import CloseIcon from '@components/svgs/CloseIcon.tsx';
44
import Search from '@components/svgs/Search.tsx';
@@ -7,6 +7,7 @@ import TechStacks from '@constant/stackList.ts';
77
import Alert from '@constant/Alert.ts';
88
import '@styles/components/TechStackSelector.scss';
99
import {DefaultStack} from '@constant/initData.ts';
10+
import {ITechStack} from '@constant/interfaces.ts';
1011

1112
interface ITechStackSelector {
1213
value: string[];
@@ -47,6 +48,53 @@ function TechStackSelector({value, placeholder='스택 입력', max=Infinity, al
4748
return 80;
4849
}, [search]);
4950

51+
const addStack = useCallback((stack: string) => {
52+
if (max !== 1 && value.length >= max) {
53+
Alert.show(`최대 ${max}개까지 선택 가능합니다.`);
54+
return;
55+
}
56+
57+
if (value.includes(stack)) {
58+
Alert.show('이미 선택된 스택입니다.');
59+
return;
60+
}
61+
62+
if (!allowCustomInput && !TechStacks.some(s => s.tagName === stack)) {
63+
Alert.show('존재하지 않는 스택입니다.');
64+
return;
65+
}
66+
67+
// max = 1 일 때, 스택만 변경되도록 설정
68+
if (max === 1) {
69+
onChange?.([stack]);
70+
saveSelectedTechStack(stack);
71+
setSearch(hideSelectedOptions ? '' : stack);
72+
console.log('search', search, stack);
73+
return;
74+
}
75+
76+
onChange?.([...value, stack]);
77+
saveSelectedTechStack(stack);
78+
setSearch('');
79+
searchRef.current?.focus();
80+
}, [max, value, allowCustomInput, onChange, hideSelectedOptions, search]);
81+
82+
function cancelSearch() {
83+
setIsShow(false);
84+
searchRef.current?.focus();
85+
popupRef.current?.blur();
86+
}
87+
88+
const deleteStack = useCallback((stack: string) => {
89+
onChange?.(value.filter(s => s !== stack));
90+
}, [value, onChange]);
91+
92+
function deleteAllStacks() {
93+
onChange?.([]);
94+
setSearch('');
95+
setIsShow(true);
96+
searchRef.current?.focus();
97+
}
5098

5199
// 검색창 키 이벤트
52100
function searchKeyEvent(e: React.KeyboardEvent<HTMLInputElement>) {
@@ -129,7 +177,7 @@ function TechStackSelector({value, placeholder='스택 입력', max=Infinity, al
129177
block: 'nearest',
130178
})
131179
}
132-
}, [focusedIndex]);
180+
}, [focusedIndex, isShow]);
133181

134182
// max = 1 이고, 유효한 search 가 아닐 때 초기화
135183
useEffect(() => {
@@ -138,54 +186,6 @@ function TechStackSelector({value, placeholder='스택 입력', max=Infinity, al
138186
}
139187
}, [isShow, value]);
140188

141-
function addStack(stack: string) {
142-
if (max !== 1 && value.length >= max) {
143-
Alert.show(`최대 ${max}개까지 선택 가능합니다.`);
144-
return;
145-
}
146-
147-
if (value.includes(stack)) {
148-
Alert.show('이미 선택된 스택입니다.');
149-
return;
150-
}
151-
152-
if (!allowCustomInput && !TechStacks.some(s => s.tagName === stack)) {
153-
Alert.show('존재하지 않는 스택입니다.');
154-
return;
155-
}
156-
157-
// max = 1 일 때, 스택만 변경되도록 설정
158-
if (max === 1) {
159-
onChange?.([stack]);
160-
saveSelectedTechStack(stack);
161-
setSearch(hideSelectedOptions ? '' : stack);
162-
console.log('search', search, stack);
163-
return;
164-
}
165-
166-
onChange?.([...value, stack]);
167-
saveSelectedTechStack(stack);
168-
setSearch('');
169-
searchRef.current?.focus();
170-
}
171-
172-
function cancelSearch() {
173-
setIsShow(false);
174-
searchRef.current?.focus();
175-
popupRef.current?.blur();
176-
}
177-
178-
function deleteStack(stack: string) {
179-
onChange?.(value.filter(s => s !== stack));
180-
}
181-
182-
function deleteAllStacks() {
183-
onChange?.([]);
184-
setSearch('');
185-
setIsShow(true);
186-
searchRef.current?.focus();
187-
}
188-
189189
return (
190190
<div className={hideSelectedOptions ? 'tech_stack_selector search_style' : 'tech_stack_selector'}
191191
ref={popupRef}>
@@ -244,13 +244,12 @@ function TechStackSelector({value, placeholder='스택 입력', max=Infinity, al
244244
)}
245245
<ul ref={ulRef} onMouseLeave={() => setFocusedIndex(-1)}>
246246
{searchedStacks.length > 0 ? searchedStacks.map((stack, index) => (
247-
<li className={'option_view ' + (focusedIndex === index ? 'selected' : '')}
248-
key={stack.tagName} tabIndex={0}
249-
onMouseOver={() => setFocusedIndex(index)}
250-
onClick={() => addStack(stack.tagName)}>
251-
<StackImage stack={stack} hasTooltip={false}/>
252-
<span>{stack.tagName}</span>
253-
</li>
247+
<SearchedStackLi key={stack.tagName}
248+
index={index}
249+
stack={stack}
250+
selected={focusedIndex === index}
251+
addStack={addStack}
252+
setFocusedIndex={setFocusedIndex}/>
254253
)) : (
255254
<li className='option_view not_searched'>
256255
<span>검색 결과가 없습니다</span>
@@ -263,4 +262,24 @@ function TechStackSelector({value, placeholder='스택 입력', max=Infinity, al
263262
);
264263
}
265264

265+
interface ISearchedStackLi {
266+
stack: ITechStack;
267+
selected: boolean;
268+
index: number;
269+
setFocusedIndex: (index: number) => void;
270+
addStack: (stack: string) => void;
271+
}
272+
273+
function SearchedStackLi({ stack, selected, index, setFocusedIndex, addStack }: ISearchedStackLi) {
274+
return (
275+
<li className={'option_view ' + (selected ? 'selected' : '')}
276+
tabIndex={0}
277+
onMouseOver={() => setFocusedIndex(index)}
278+
onClick={() => addStack(stack.tagName)}>
279+
<StackImage stack={stack} hasTooltip={false}/>
280+
<span>{stack.tagName}</span>
281+
</li>
282+
);
283+
}
284+
266285
export default TechStackSelector;

src/index.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
background-color: white;
2323

2424
font-synthesis: none;
25-
text-rendering: optimizeLegibility;
26-
/*text-rendering: auto;*/
25+
/*text-rendering: optimizeLegibility;*/
26+
text-rendering: auto;
2727
-webkit-font-smoothing: antialiased;
2828
-moz-osx-font-smoothing: grayscale;
2929
-webkit-text-size-adjust: 100%;
@@ -37,6 +37,7 @@ a {
3737
}
3838
a:hover, a:focus-visible, a.selected {
3939
color: var(--color-primary);
40+
font-weight: var(--size-semibold);
4041
}
4142

4243
body {
@@ -169,6 +170,7 @@ button.link {
169170
}
170171
button.link:hover {
171172
color: var(--color-primary);
173+
font-weight: var(--size-semibold);
172174
background-color: transparent;
173175
}
174176

src/styles/MainProjectPage.scss

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ div.team_title_layout {
808808

809809
.stack_layout {
810810
position: relative;
811+
line-height: 0;
811812

812813
img {
813814
padding: 4px;
@@ -821,19 +822,20 @@ div.team_title_layout {
821822
position: absolute;
822823
top: 100%;
823824
left: 50%;
824-
transform: translate(-50%, -10%);
825+
transform: translate(-50%, 10%);
825826
border-radius: 5px;
826-
padding: 4px 8px;
827+
padding: 6px 8px;
827828
z-index: 10;
828829

829830
color: white;
830831
background: #333;
832+
line-height: 150%;
831833
opacity: 0;
832834
transition: opacity 0.2s ease;
835+
}
833836

834-
&.visible {
835-
opacity: 1;
836-
}
837+
img:hover + span {
838+
opacity: 1;
837839
}
838840
}
839841

src/styles/components/MentorCard.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
margin: 0;
6767
}
6868

69-
h5 {
69+
h4 {
7070
color: var(--gray, #777);
7171
font-size: 12px;
7272
font-weight: 500;

src/styles/components/UserCard.scss

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@
8080
margin: 0;
8181
}
8282

83-
h5 {
84-
color: var(--gray, #777);
85-
font-size: 12px;
86-
font-weight: 500;
87-
line-height: 150%;
88-
}
83+
//h4 {
84+
// color: var(--gray, #777);
85+
// font-size: 12px;
86+
// font-weight: 500;
87+
// line-height: 150%;
88+
//}
8989
}
9090

9191
.user_feedback_layout {
@@ -144,7 +144,7 @@
144144
div.user_heart_layout {
145145
display: flex;
146146
justify-content: left;
147-
margin-top: 4px;
147+
margin-top: 8px;
148148
}
149149

150150
.user_position_layout {

0 commit comments

Comments
 (0)