From 7005b341881c9682cb69faa2fce2605c0012d786 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Sun, 10 Nov 2024 23:36:48 +0900 Subject: [PATCH 01/17] =?UTF-8?q?feat:=20es-hangul=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/search/hangulUtils.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/utils/search/hangulUtils.ts diff --git a/src/utils/search/hangulUtils.ts b/src/utils/search/hangulUtils.ts new file mode 100644 index 0000000..81bd734 --- /dev/null +++ b/src/utils/search/hangulUtils.ts @@ -0,0 +1,17 @@ +// src/utils/hangulUtils.ts +import { disassembleCompleteCharacter } from 'es-hangul'; + +/** + * 한글 문자열을 초성으로 변환합니다. + * @param text 변환할 한글 문자열 + * @returns 초성 문자열 + */ +export const getInitials = (text: string): string => { + return text + .split('') + .map(char => { + const decomposed = disassembleCompleteCharacter(char); + return decomposed ? decomposed.choseong : char; + }) + .join(''); +}; From 8a14aad4528b3d0e3387866a63d390d2e048c8da Mon Sep 17 00:00:00 2001 From: anhye0n Date: Sun, 10 Nov 2024 23:37:24 +0900 Subject: [PATCH 02/17] =?UTF-8?q?fix:=20merge=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/assets/icons/Icon.tsx | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/ui/assets/icons/Icon.tsx diff --git a/src/ui/assets/icons/Icon.tsx b/src/ui/assets/icons/Icon.tsx new file mode 100644 index 0000000..1c63cc5 --- /dev/null +++ b/src/ui/assets/icons/Icon.tsx @@ -0,0 +1,44 @@ +import React from 'react'; + +interface IconProps extends React.SVGProps { + width?: number; + height?: number; + fill?: string; +} + +const SearchIcon: React.FC = ({ + width = 26, + height = 26, + fill = 'none', + ...props +}) => ( + + + + + + + + +); + +export default SearchIcon; From ecf1c5ef4e68ed8e48bdb32d4e6cf840cf3570a6 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Sun, 10 Nov 2024 23:37:55 +0900 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20=EA=B5=90=EC=88=98=EB=8B=98=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/data/professors.json | 65 ++++++++++++++++++++++++++++++++++ src/utils/types/Professor.ts | 7 ++++ 2 files changed, 72 insertions(+) create mode 100644 src/utils/data/professors.json create mode 100644 src/utils/types/Professor.ts diff --git a/src/utils/data/professors.json b/src/utils/data/professors.json new file mode 100644 index 0000000..ddd09c1 --- /dev/null +++ b/src/utils/data/professors.json @@ -0,0 +1,65 @@ +[ + { + "name": "권순일", + "labLocation": "대양AI 624호", + "phoneNumber": "02-3408-3847", + "email": "sikwon@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "name": "백성욱", + "labLocation": "대양AI 622호", + "phoneNumber": "02-3408-3797", + "email": "sbaik@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "name": "이종원", + "labLocation": "대양AI 619호", + "phoneNumber": "02-3408-3798", + "email": "jwlee@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "name": "송오영", + "labLocation": "대양AI 625호", + "phoneNumber": "02-3408-3830", + "email": "oysong@sejong.edu", + "department": "소프트웨어학과" + }, + { + "name": "최준연", + "labLocation": "대양AI 620호", + "phoneNumber": "02-3408-3887", + "email": "zoon@sejong.edu", + "department": "소프트웨어학과" + }, + { + "name": "박상일", + "labLocation": "대양AI 626호", + "phoneNumber": "02-3408-3832", + "email": "sipark@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "name": "변재욱", + "labLocation": "대양AI 604호", + "phoneNumber": "02-3408-1847", + "email": "jwbyun@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "name": "이은상", + "labLocation": "대양AI 621호", + "phoneNumber": "02-3408-2975", + "email": "eslee3209@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "name": "정승화", + "labLocation": "대양AI 623호", + "phoneNumber": "02-3408-3795", + "email": "seunghwajeong@sejong.ac.kr", + "department": "소프트웨어학과" + } +] diff --git a/src/utils/types/Professor.ts b/src/utils/types/Professor.ts new file mode 100644 index 0000000..22b2141 --- /dev/null +++ b/src/utils/types/Professor.ts @@ -0,0 +1,7 @@ +export interface Professor { + name: string; + labLocation: string; + phoneNumber: string; + email: string; + department: string; +} From 7cb2963112371cdb60a3174bf3aee41b9b729462 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Sun, 10 Nov 2024 23:38:18 +0900 Subject: [PATCH 04/17] =?UTF-8?q?chore:=20npm=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 56 +++++++++++++++++++++++++++++++++++++++++------ package.json | 3 +++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9540ef..a1fcf0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,15 @@ "dependencies": { "@tanstack/react-query": "^5.59.8", "@tanstack/react-query-devtools": "^5.59.8", + "@types/react-transition-group": "^4.4.11", "axios": "^1.7.7", + "es-hangul": "^2.2.1", "jotai": "^2.10.0", "overlay-kit": "^1.4.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", + "react-transition-group": "^4.4.5", "styled-components": "^6.1.13" }, "devDependencies": { @@ -391,7 +394,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", - "dev": true, "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -3047,7 +3049,6 @@ "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/qs": { @@ -3068,7 +3069,6 @@ "version": "18.3.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -3085,6 +3085,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.20.6", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", @@ -4340,6 +4349,16 @@ "dev": true, "license": "MIT" }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -4441,6 +4460,17 @@ "node": ">= 0.4" } }, + "node_modules/es-hangul": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/es-hangul/-/es-hangul-2.2.1.tgz", + "integrity": "sha512-Ra5msEOzRdUBoKsdnkzGthstjehalXJAoG4EBZd2JsYErxiX3I7qQZNigbhowLhwDEt3qHcBCJp6PiOdp99yZw==", + "license": "MIT", + "workspaces": [ + ".", + "docs", + "benchmarks" + ] + }, "node_modules/es-module-lexer": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", @@ -6772,7 +6802,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7271,7 +7300,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -7483,7 +7511,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-refresh": { @@ -7528,6 +7555,22 @@ "react-dom": ">=16.8" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/recast": { "version": "0.23.9", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.9.tgz", @@ -7586,7 +7629,6 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true, "license": "MIT" }, "node_modules/rehype-external-links": { diff --git a/package.json b/package.json index be2e676..3fd6e0c 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,15 @@ "dependencies": { "@tanstack/react-query": "^5.59.8", "@tanstack/react-query-devtools": "^5.59.8", + "@types/react-transition-group": "^4.4.11", "axios": "^1.7.7", + "es-hangul": "^2.2.1", "jotai": "^2.10.0", "overlay-kit": "^1.4.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", + "react-transition-group": "^4.4.5", "styled-components": "^6.1.13" }, "devDependencies": { From 161d6ebc23e914bc22574d2aee85f33113eaf8c0 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Sun, 10 Nov 2024 23:38:47 +0900 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EC=95=A0?= =?UTF-8?q?=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98,=20=EC=B4=88=EC=84=B1=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EC=B6=94=EA=B0=80(=EA=B5=90=EC=88=98?= =?UTF-8?q?=EB=8B=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search/ui/components/ModalSearch.tsx | 37 ++++ .../search/ui/components/SearchInput.tsx | 160 ++++++++++++++++-- .../search/ui/components/SearchResults.tsx | 126 ++++++++++++++ src/pages/Home.tsx | 69 +++++++- src/ui/styles/pallete/searchPalette.ts | 20 +++ 5 files changed, 400 insertions(+), 12 deletions(-) create mode 100644 src/features/search/ui/components/ModalSearch.tsx create mode 100644 src/features/search/ui/components/SearchResults.tsx create mode 100644 src/ui/styles/pallete/searchPalette.ts diff --git a/src/features/search/ui/components/ModalSearch.tsx b/src/features/search/ui/components/ModalSearch.tsx new file mode 100644 index 0000000..0cc9efe --- /dev/null +++ b/src/features/search/ui/components/ModalSearch.tsx @@ -0,0 +1,37 @@ +import { overlay } from 'overlay-kit'; +import React from 'react'; +import styled from 'styled-components'; + +interface ModalSearchProps { + open: boolean; + onClose: () => void; + onExit: () => void; +} + +const ModalSearch: React.FC = ({ open, onClose, onExit }) => { + return ( + <> + + + + + ); +}; + +export default ModalSearch; + +const ModalBackground = styled.div` + background: rgba(0, 0, 0, 0.5); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +`; diff --git a/src/features/search/ui/components/SearchInput.tsx b/src/features/search/ui/components/SearchInput.tsx index 6371b62..82e48a3 100644 --- a/src/features/search/ui/components/SearchInput.tsx +++ b/src/features/search/ui/components/SearchInput.tsx @@ -1,29 +1,169 @@ +// src/components/SearchInput.tsx +import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; +import { SearchResults } from '@semo-client/features/search/ui/components/SearchResults.tsx'; +import SearchIcon from '@semo-client/ui/assets/icons/Icon'; +import searchPalette from '@semo-client/ui/styles/pallete/searchPalette.ts'; +import { Professor } from '@semo-utils/types/Professor.ts'; -export const SearchInput = () => { - return ; +interface SearchInputProps { + query: string; + setQuery: (query: string) => void; + onChange: (e: React.ChangeEvent) => void; + onSubmit: () => void; + results: Professor[]; +} + +export const SearchInput: React.FC = ({ + query, + setQuery, + onChange, + onSubmit, + results, +}) => { + const [isExpanded, setIsExpanded] = useState(false); + const [isOverlayVisible, setIsOverlayVisible] = useState(false); + + const handleExpand = () => { + setIsExpanded(true); + setIsOverlayVisible(true); + }; + + const handleCollapse = () => { + setIsExpanded(false); + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + onSubmit(); + handleCollapse(); + } + }; + + useEffect(() => { + if (!isExpanded) { + // 오버레이를 애니메이션 후에 숨김 + const timeoutId = setTimeout(() => { + setIsOverlayVisible(false); + }, 300); // 트랜지션 시간과 동일하게 설정 + + // 검색창 초기화 + setQuery(''); + + return () => clearTimeout(timeoutId); + } else { + setIsOverlayVisible(true); + } + }, [isExpanded]); + + return ( + <> + + + + + + { + e.stopPropagation(); + handleExpand(); + }} + isExpanded={isExpanded} + autoFocus={isExpanded} + /> + + {results.length != 0 && ( + + )} + + + {isOverlayVisible && ( + + )} + + ); }; -const StyledInput = styled.input` +const SearchInputWrapper = styled.div<{ isExpanded: boolean }>` + position: absolute; + top: ${({ isExpanded }) => (isExpanded ? '40%' : '65%')}; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1000; + width: ${({ isExpanded }) => (isExpanded ? '50%' : '500px')}; + transition: 0.3s ease-in-out; +`; + +const InputContainer = styled.div<{ isExpanded: boolean }>` + display: flex; + align-items: center; width: 100%; height: 54px; - border-radius: 8px; + border-radius: ${({ isExpanded }) => + isExpanded ? '8px 8px 0 0' : '8px'}; /* 조건부 border-radius */ + background-color: ${({ isExpanded }) => + isExpanded ? searchPalette.blackGray[90] : searchPalette.blackGray[70]}; + border: 1px solid ${searchPalette.whiteGray[30]}; + padding: 0 14px; + gap: 10px; + font-size: 20px; + color: #fff; + + &:focus-within { + outline: none; + border-color: ${searchPalette.whiteGray[50]}; + background: ${({ isExpanded }) => + isExpanded ? searchPalette.whiteGray[70] : '#000000'}; + } + + /* 애니메이션 효과 */ + transition: all 0.3s ease-in-out; +`; - background-color: rgba(0, 0, 0, 0.7); - border: 1px solid rgba(255, 255, 255, 0.3); - padding: 10px 14px; +const SearchIconWrapper = styled.div` display: flex; - gap: 10px; align-items: center; + justify-content: center; +`; +const StyledInput = styled.input<{ isExpanded: boolean }>` + flex: 1; + height: 100%; + border: none; + background: transparent; + color: ${({ isExpanded }) => + isExpanded ? searchPalette.black : searchPalette.white}; font-size: 20px; - color: #fff; + position: relative; + z-index: 2; &::placeholder { - color: rgba(255, 255, 255, 0.5); + color: ${({ isExpanded }) => + isExpanded ? '#000' : searchPalette.whiteGray[50]}; } &:focus { outline: none; } `; + +const Overlay = styled.div<{ isExpanded: boolean }>` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: ${searchPalette.blackGray[90]}; + z-index: 999; + opacity: ${({ isExpanded }) => (isExpanded ? 1 : 0)}; + transition: opacity 0.3s ease-in-out; +`; diff --git a/src/features/search/ui/components/SearchResults.tsx b/src/features/search/ui/components/SearchResults.tsx new file mode 100644 index 0000000..69ca359 --- /dev/null +++ b/src/features/search/ui/components/SearchResults.tsx @@ -0,0 +1,126 @@ +// src/components/SearchResults.tsx +import React from 'react'; +import styled from 'styled-components'; +import searchPalette from '@semo-client/ui/styles/pallete/searchPalette.ts'; +import { Professor } from '@semo-utils/types/Professor.ts'; + +interface SearchResultsProps { + value: string; + results: Professor[]; +} + +export const SearchResults: React.FC = ({ + value, + results, +}) => { + return ( + + {results.map((professor, index) => ( + + + + + {professor.name} 교수님 + {professor.department} + + +
+
+ 연구실 +

{professor.labLocation}

+
+
+ 전화번호 +

{professor.phoneNumber}

+
+
+ 이메일 +

{professor.email}

+
+
+ ))} +
+ ); +}; + +const ResultsContainer = styled.div<{ value: string }>` + position: absolute; + width: 100%; + background: ${searchPalette.whiteGray[70]}; + border-bottom-right-radius: 8px; + border-bottom-left-radius: 8px; + opacity: ${({ value }) => (value ? 1 : 0)}; + padding: 14px; +`; + +const ResultItem = styled.div` + background-color: ${searchPalette.blackGray[10]}; + backdrop-filter: blur(1px); + border: 2px solid ${searchPalette.blackGray[10]}; + padding: 12px; + border-radius: 8px; + margin-bottom: 10px; + color: #fff; +`; + +const Profile = styled.div` + display: flex; +`; + +const ProfileInfo = styled.div` + display: flex; + flex-direction: column; + margin-left: 10px; +`; + +const ProfileImage = styled.img` + background: #fff; + border-radius: 50%; + width: 36px; + height: 36px; +`; + +const Name = styled.p` + font-size: 16px; + font-weight: 700; + color: ${searchPalette.black}; +`; + +const Department = styled.p` + font-size: 14px; + font-weight: 400; + color: ${searchPalette.black}; +`; + +const Hr = styled.hr` + width: 100%; + margin-top: 10px; + margin-bottom: 10px; + border: 1px solid ${searchPalette.blackGray[30]}; +`; + +const Details = styled.div` + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 8px; + p { + color: ${searchPalette.black}; + font-size: 16px; + font-weight: 400; + } + &:last-child { + margin-bottom: 0; + } +`; + +const Badge = styled.span` + display: flex; + padding: 2px 3px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 4px; + font-size: 14px; + background: #818181; +`; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index d8777fc..8fd4f1c 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,19 +1,72 @@ +import debounce from 'lodash/debounce'; +import { useCallback, useState } from 'react'; import styled from 'styled-components'; import { BackgroundImageView } from '@semo-client/features/background-image/ui/components/BackgroundImageView'; import { Clock } from '@semo-client/features/clock/ui/components/Clock'; import { HeaderView } from '@semo-client/features/header/ui/components/HeaderView'; import { NoticeView } from '@semo-client/features/notice/ui/components/NoticeView'; import { SearchInput } from '@semo-client/features/search/ui/components/SearchInput'; +import { SearchResults } from '@semo-client/features/search/ui/components/SearchResults'; import { TrendingKeywords } from '@semo-client/features/search/ui/components/TrendingKeywords'; import { LoginButtonView } from '@semo-client/features/users/ui/components/LoginButtonView'; +import professorsData from '@semo-utils/data/professors.json'; +import { getInitials } from '@semo-utils/search/hangulUtils'; +import { Professor } from '@semo-utils/types/Professor'; +import { Controls } from '@storybook/blocks'; /** * TODO 각 요소 컴포넌트 에는 추가 스타일(여백,마진) 들어있지 않은 순수 요소 컴포넌트 * 각 요소의 마진 요소들은 (레이아웃 잡기) 여기서 container 컴포넌트에서 잡아준다. */ export const Home = () => { - // load user login status + const [query, setQuery] = useState(''); + // 검색 결과 반영 + const [results, setResults] = useState([]); + /** + * 검색어에 따라 교수님 데이터를 필터링합니다. + * 전체 이름, 초성, 일부 이름을 포함하는지 확인합니다. + * @param searchQuery 사용자 입력 검색어 + */ + const fetchData = (searchQuery: string) => { + if (searchQuery.trim() === '') { + setResults([]); + return; + } + + const normalizedQuery = searchQuery.trim().toLowerCase(); + const queryInitials = getInitials(normalizedQuery); + + const filtered = professorsData.filter(professor => { + const name = professor.name.toLowerCase(); + const initials = getInitials(professor.name.toLowerCase()); + + return ( + name.includes(normalizedQuery) || // 전체 이름 또는 일부 이름 포함 + initials.includes(normalizedQuery) // 초성 포함 + ); + }); + + setResults(filtered); + }; + + const debouncedSearch = useCallback( + debounce((searchTerm: string) => { + fetchData(searchTerm); + }, 300), + [], + ); + + const handleChange = (e: React.ChangeEvent) => { + const input = e.target.value; + setQuery(input); + debouncedSearch(input); + }; + + const handleSubmit = () => { + debouncedSearch.cancel(); + fetchData(query); + }; return ( @@ -29,7 +82,14 @@ export const Home = () => { {/* Search */} - + + @@ -100,3 +160,8 @@ const AppContainer = styled.main` display: none; } `; + +const DummyInputBox = styled.div` + width: 100%; + height: 90px; +`; diff --git a/src/ui/styles/pallete/searchPalette.ts b/src/ui/styles/pallete/searchPalette.ts new file mode 100644 index 0000000..2512f4c --- /dev/null +++ b/src/ui/styles/pallete/searchPalette.ts @@ -0,0 +1,20 @@ +export const searchPalette = { + // 흑색 계열 + black: '#000000', + blackGray: { + 10: 'rgba(0, 0, 0, 0.1)', + 30: 'rgba(0, 0, 0, 0.3)', + 50: 'rgba(0, 0, 0, 0.5)', + 70: 'rgba(0, 0, 0, 0.7)', + 90: 'rgba(0, 0, 0, 0.9)', + }, + // 백색 계열 + white: '#FFFFFF', + whiteGray: { + 30: 'rgba(255, 255, 255, 0.3)', + 50: 'rgba(255, 255, 255, 0.5)', + 70: 'rgba(255, 255, 255, 0.7)', + }, +}; + +export default searchPalette; From 324065ae78a96e17d6c16e7a21345e479f07317f Mon Sep 17 00:00:00 2001 From: anhye0n Date: Sun, 10 Nov 2024 23:51:27 +0900 Subject: [PATCH 06/17] =?UTF-8?q?fix:=20merge=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/search/ui/components/SearchInput.tsx | 4 ++-- src/pages/Home.tsx | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/features/search/ui/components/SearchInput.tsx b/src/features/search/ui/components/SearchInput.tsx index 82e48a3..6040b4d 100644 --- a/src/features/search/ui/components/SearchInput.tsx +++ b/src/features/search/ui/components/SearchInput.tsx @@ -1,5 +1,4 @@ -// src/components/SearchInput.tsx -import React, { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; import styled from 'styled-components'; import { SearchResults } from '@semo-client/features/search/ui/components/SearchResults.tsx'; import SearchIcon from '@semo-client/ui/assets/icons/Icon'; @@ -133,6 +132,7 @@ const SearchIconWrapper = styled.div` display: flex; align-items: center; justify-content: center; + transition: all 0.3s ease-in-out; `; const StyledInput = styled.input<{ isExpanded: boolean }>` diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 8fd4f1c..e3a9333 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -6,7 +6,6 @@ import { Clock } from '@semo-client/features/clock/ui/components/Clock'; import { HeaderView } from '@semo-client/features/header/ui/components/HeaderView'; import { NoticeView } from '@semo-client/features/notice/ui/components/NoticeView'; import { SearchInput } from '@semo-client/features/search/ui/components/SearchInput'; -import { SearchResults } from '@semo-client/features/search/ui/components/SearchResults'; import { TrendingKeywords } from '@semo-client/features/search/ui/components/TrendingKeywords'; import { LoginButtonView } from '@semo-client/features/users/ui/components/LoginButtonView'; import professorsData from '@semo-utils/data/professors.json'; From 14d8f9d35074ed36a2e61a7d7bfcaa08c852494c Mon Sep 17 00:00:00 2001 From: anhye0n Date: Sun, 10 Nov 2024 23:54:43 +0900 Subject: [PATCH 07/17] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/search/ui/components/SearchResults.tsx | 1 - src/ui/styles/pallete/searchPalette.ts | 2 -- src/utils/search/hangulUtils.ts | 1 - 3 files changed, 4 deletions(-) diff --git a/src/features/search/ui/components/SearchResults.tsx b/src/features/search/ui/components/SearchResults.tsx index 69ca359..3c2b000 100644 --- a/src/features/search/ui/components/SearchResults.tsx +++ b/src/features/search/ui/components/SearchResults.tsx @@ -1,4 +1,3 @@ -// src/components/SearchResults.tsx import React from 'react'; import styled from 'styled-components'; import searchPalette from '@semo-client/ui/styles/pallete/searchPalette.ts'; diff --git a/src/ui/styles/pallete/searchPalette.ts b/src/ui/styles/pallete/searchPalette.ts index 2512f4c..9676185 100644 --- a/src/ui/styles/pallete/searchPalette.ts +++ b/src/ui/styles/pallete/searchPalette.ts @@ -1,5 +1,4 @@ export const searchPalette = { - // 흑색 계열 black: '#000000', blackGray: { 10: 'rgba(0, 0, 0, 0.1)', @@ -8,7 +7,6 @@ export const searchPalette = { 70: 'rgba(0, 0, 0, 0.7)', 90: 'rgba(0, 0, 0, 0.9)', }, - // 백색 계열 white: '#FFFFFF', whiteGray: { 30: 'rgba(255, 255, 255, 0.3)', diff --git a/src/utils/search/hangulUtils.ts b/src/utils/search/hangulUtils.ts index 81bd734..2fd3b79 100644 --- a/src/utils/search/hangulUtils.ts +++ b/src/utils/search/hangulUtils.ts @@ -1,4 +1,3 @@ -// src/utils/hangulUtils.ts import { disassembleCompleteCharacter } from 'es-hangul'; /** From 5e77625a5ae0293f922f3023cf442a7a652dd08a Mon Sep 17 00:00:00 2001 From: anhye0n Date: Mon, 11 Nov 2024 00:01:42 +0900 Subject: [PATCH 08/17] =?UTF-8?q?fix:=20=ED=83=80=EC=9E=85=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/search/ui/components/SearchResults.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/features/search/ui/components/SearchResults.tsx b/src/features/search/ui/components/SearchResults.tsx index 3c2b000..d384790 100644 --- a/src/features/search/ui/components/SearchResults.tsx +++ b/src/features/search/ui/components/SearchResults.tsx @@ -17,7 +17,9 @@ export const SearchResults: React.FC = ({ {results.map((professor, index) => ( - + // TODO: 이미지 추가 필요 + {/**/} + {professor.name} 교수님 {professor.department} @@ -72,7 +74,7 @@ const ProfileInfo = styled.div` margin-left: 10px; `; -const ProfileImage = styled.img` +const ProfileImage = styled.span` background: #fff; border-radius: 50%; width: 36px; From 89220813fed12e134890533eb5c0836f391c8391 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Mon, 11 Nov 2024 00:04:00 +0900 Subject: [PATCH 09/17] =?UTF-8?q?chore:=20react-transition-group=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 44 +++++++------------------------------------- package.json | 2 -- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1fcf0e..557b8ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "dependencies": { "@tanstack/react-query": "^5.59.8", "@tanstack/react-query-devtools": "^5.59.8", - "@types/react-transition-group": "^4.4.11", "axios": "^1.7.7", "es-hangul": "^2.2.1", "jotai": "^2.10.0", @@ -18,7 +17,6 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", - "react-transition-group": "^4.4.5", "styled-components": "^6.1.13" }, "devDependencies": { @@ -394,6 +392,7 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "dev": true, "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -3049,6 +3048,7 @@ "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "devOptional": true, "license": "MIT" }, "node_modules/@types/qs": { @@ -3069,6 +3069,7 @@ "version": "18.3.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -3085,15 +3086,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", - "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/resolve": { "version": "1.20.6", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", @@ -4349,16 +4341,6 @@ "dev": true, "license": "MIT" }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -6802,6 +6784,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7300,6 +7283,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -7511,6 +7495,7 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, "license": "MIT" }, "node_modules/react-refresh": { @@ -7555,22 +7540,6 @@ "react-dom": ">=16.8" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/recast": { "version": "0.23.9", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.9.tgz", @@ -7629,6 +7598,7 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, "license": "MIT" }, "node_modules/rehype-external-links": { diff --git a/package.json b/package.json index 3fd6e0c..881270e 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "dependencies": { "@tanstack/react-query": "^5.59.8", "@tanstack/react-query-devtools": "^5.59.8", - "@types/react-transition-group": "^4.4.11", "axios": "^1.7.7", "es-hangul": "^2.2.1", "jotai": "^2.10.0", @@ -36,7 +35,6 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", - "react-transition-group": "^4.4.5", "styled-components": "^6.1.13" }, "devDependencies": { From 67473702e0fdc2f047eeb6d1ce750929ee94f0f0 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Mon, 11 Nov 2024 00:16:30 +0900 Subject: [PATCH 10/17] =?UTF-8?q?refactor:=20=EA=B5=90=EC=88=98=EB=8B=98?= =?UTF-8?q?=20types,=20data=20=EA=B2=BD=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/{utils => features/search}/data/professors.json | 0 src/{utils/types => features/search/models}/Professor.ts | 0 src/features/search/ui/components/SearchInput.tsx | 2 +- src/features/search/ui/components/SearchResults.tsx | 4 ++-- src/pages/Home.tsx | 4 ++-- 5 files changed, 5 insertions(+), 5 deletions(-) rename src/{utils => features/search}/data/professors.json (100%) rename src/{utils/types => features/search/models}/Professor.ts (100%) diff --git a/src/utils/data/professors.json b/src/features/search/data/professors.json similarity index 100% rename from src/utils/data/professors.json rename to src/features/search/data/professors.json diff --git a/src/utils/types/Professor.ts b/src/features/search/models/Professor.ts similarity index 100% rename from src/utils/types/Professor.ts rename to src/features/search/models/Professor.ts diff --git a/src/features/search/ui/components/SearchInput.tsx b/src/features/search/ui/components/SearchInput.tsx index 6040b4d..87eeac3 100644 --- a/src/features/search/ui/components/SearchInput.tsx +++ b/src/features/search/ui/components/SearchInput.tsx @@ -1,9 +1,9 @@ import { useEffect, useState } from 'react'; import styled from 'styled-components'; +import { Professor } from '@semo-client/features/search/models/Professor.ts'; import { SearchResults } from '@semo-client/features/search/ui/components/SearchResults.tsx'; import SearchIcon from '@semo-client/ui/assets/icons/Icon'; import searchPalette from '@semo-client/ui/styles/pallete/searchPalette.ts'; -import { Professor } from '@semo-utils/types/Professor.ts'; interface SearchInputProps { query: string; diff --git a/src/features/search/ui/components/SearchResults.tsx b/src/features/search/ui/components/SearchResults.tsx index d384790..407cbe6 100644 --- a/src/features/search/ui/components/SearchResults.tsx +++ b/src/features/search/ui/components/SearchResults.tsx @@ -1,7 +1,7 @@ import React from 'react'; import styled from 'styled-components'; +import { Professor } from '@semo-client/features/search/models/Professor.ts'; import searchPalette from '@semo-client/ui/styles/pallete/searchPalette.ts'; -import { Professor } from '@semo-utils/types/Professor.ts'; interface SearchResultsProps { value: string; @@ -17,7 +17,7 @@ export const SearchResults: React.FC = ({ {results.map((professor, index) => ( - // TODO: 이미지 추가 필요 + {/*// TODO: 이미지 추가 필요*/} {/**/} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index e3a9333..08d1b87 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -5,12 +5,12 @@ import { BackgroundImageView } from '@semo-client/features/background-image/ui/c import { Clock } from '@semo-client/features/clock/ui/components/Clock'; import { HeaderView } from '@semo-client/features/header/ui/components/HeaderView'; import { NoticeView } from '@semo-client/features/notice/ui/components/NoticeView'; +import professorsData from '@semo-client/features/search/data/professors.json'; +import { Professor } from '@semo-client/features/search/models/Professor.ts'; import { SearchInput } from '@semo-client/features/search/ui/components/SearchInput'; import { TrendingKeywords } from '@semo-client/features/search/ui/components/TrendingKeywords'; import { LoginButtonView } from '@semo-client/features/users/ui/components/LoginButtonView'; -import professorsData from '@semo-utils/data/professors.json'; import { getInitials } from '@semo-utils/search/hangulUtils'; -import { Professor } from '@semo-utils/types/Professor'; import { Controls } from '@storybook/blocks'; /** From 228549f0481b4f783f33d0f62b56699a7fa56230 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Wed, 13 Nov 2024 16:32:04 +0900 Subject: [PATCH 11/17] =?UTF-8?q?feat:=20useSearchOverlayController=20hook?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/hooks/useSearchOverlayController.tsx | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/features/search/ui/hooks/useSearchOverlayController.tsx diff --git a/src/features/search/ui/hooks/useSearchOverlayController.tsx b/src/features/search/ui/hooks/useSearchOverlayController.tsx new file mode 100644 index 0000000..0bb1c0c --- /dev/null +++ b/src/features/search/ui/hooks/useSearchOverlayController.tsx @@ -0,0 +1,58 @@ +import React, { useState, useCallback } from 'react'; + +interface UseSearchOverlayControllerProps { + onSubmit: () => void; + setQuery: (query: string) => void; +} + +interface UseSearchOverlayControllerReturn { + isExpanded: boolean; + isOverlayVisible: boolean; + handleExpand: () => void; + handleCollapse: () => void; + handleKeyPress: (e: React.KeyboardEvent) => void; + handleOverlayAnimationEnd: () => void; +} + +export const useSearchOverlayController = ({ + onSubmit, + setQuery, +}: UseSearchOverlayControllerProps) => { + const [isExpanded, setIsExpanded] = useState(false); + const [isOverlayVisible, setIsOverlayVisible] = useState(false); + + const handleExpand = useCallback(() => { + setIsOverlayVisible(true); + setIsExpanded(true); + }, []); + + const handleCollapse = useCallback(() => { + setIsExpanded(false); + }, []); + + const handleKeyPress = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + onSubmit(); + handleCollapse(); + } + }, + [onSubmit, handleCollapse], + ); + + const handleOverlayAnimationEnd = useCallback(() => { + if (!isExpanded) { + setIsOverlayVisible(false); + setQuery(''); + } + }, [isExpanded, setQuery]); + + return { + isExpanded, + isOverlayVisible, + handleExpand, + handleCollapse, + handleKeyPress, + handleOverlayAnimationEnd, + }; +}; From 57bec5a2e41aed973f96c46ee86271e163ffad29 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Wed, 13 Nov 2024 16:32:34 +0900 Subject: [PATCH 12/17] =?UTF-8?q?feat:=20searchPalette=20palette=EC=97=90?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/styles/pallete/searchPalette.ts | 5 ++--- src/ui/styles/theme.ts | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ui/styles/pallete/searchPalette.ts b/src/ui/styles/pallete/searchPalette.ts index 9676185..103a948 100644 --- a/src/ui/styles/pallete/searchPalette.ts +++ b/src/ui/styles/pallete/searchPalette.ts @@ -13,6 +13,5 @@ export const searchPalette = { 50: 'rgba(255, 255, 255, 0.5)', 70: 'rgba(255, 255, 255, 0.7)', }, -}; - -export default searchPalette; +} as const; +export type searchPalette = typeof searchPalette; diff --git a/src/ui/styles/theme.ts b/src/ui/styles/theme.ts index 1eadfe1..52e379b 100644 --- a/src/ui/styles/theme.ts +++ b/src/ui/styles/theme.ts @@ -4,12 +4,14 @@ import { buttons, dims, pallete, + searchPalette, shadows, texts, } from './colors'; export const theme = { pallete, + searchPalette, backgrounds, texts, shadows, From fd27d65032c73a90b1405854caeb5350955d21de Mon Sep 17 00:00:00 2001 From: anhye0n Date: Wed, 13 Nov 2024 16:34:53 +0900 Subject: [PATCH 13/17] =?UTF-8?q?chore:=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20Mo?= =?UTF-8?q?dalSearch.tsx=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search/ui/components/ModalSearch.tsx | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 src/features/search/ui/components/ModalSearch.tsx diff --git a/src/features/search/ui/components/ModalSearch.tsx b/src/features/search/ui/components/ModalSearch.tsx deleted file mode 100644 index 0cc9efe..0000000 --- a/src/features/search/ui/components/ModalSearch.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { overlay } from 'overlay-kit'; -import React from 'react'; -import styled from 'styled-components'; - -interface ModalSearchProps { - open: boolean; - onClose: () => void; - onExit: () => void; -} - -const ModalSearch: React.FC = ({ open, onClose, onExit }) => { - return ( - <> - - - - - ); -}; - -export default ModalSearch; - -const ModalBackground = styled.div` - background: rgba(0, 0, 0, 0.5); - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -`; From 585322a4a2413b77920fe8d06810a5c82add89cd Mon Sep 17 00:00:00 2001 From: anhye0n Date: Wed, 13 Nov 2024 16:35:10 +0900 Subject: [PATCH 14/17] =?UTF-8?q?chore:=20Palette=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/styles/colors/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/styles/colors/index.ts b/src/ui/styles/colors/index.ts index 1161b2c..d71cc47 100644 --- a/src/ui/styles/colors/index.ts +++ b/src/ui/styles/colors/index.ts @@ -3,5 +3,6 @@ export { borders } from './borders'; export { buttons } from './buttons'; export { dims } from './dims'; export { pallete } from '../pallete/pallete'; +export { searchPalette } from '../pallete//searchPalette'; export { shadows } from './shadows'; export { texts } from './texts'; From 931c7568878d9e5ec56c99d1c538116bd1994556 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Wed, 13 Nov 2024 16:36:09 +0900 Subject: [PATCH 15/17] =?UTF-8?q?chore:=20=ED=99=95=EC=9E=A5=EC=9E=90=20.t?= =?UTF-8?q?s,=20.tsx=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search/ui/components/SearchInput.tsx | 121 +++++++++--------- .../search/ui/components/SearchResults.tsx | 55 ++++++-- src/main.tsx | 2 +- src/pages/Home.tsx | 3 +- 4 files changed, 104 insertions(+), 77 deletions(-) diff --git a/src/features/search/ui/components/SearchInput.tsx b/src/features/search/ui/components/SearchInput.tsx index 87eeac3..85a72ae 100644 --- a/src/features/search/ui/components/SearchInput.tsx +++ b/src/features/search/ui/components/SearchInput.tsx @@ -1,9 +1,9 @@ -import { useEffect, useState } from 'react'; -import styled from 'styled-components'; -import { Professor } from '@semo-client/features/search/models/Professor.ts'; -import { SearchResults } from '@semo-client/features/search/ui/components/SearchResults.tsx'; +import React from 'react'; +import styled, { keyframes } from 'styled-components'; +import { Professor } from '@semo-client/features/search/models/Professor'; +import { SearchResults } from '@semo-client/features/search/ui/components/SearchResults'; +import { useSearchOverlayController } from '@semo-client/features/search/ui/hooks/useSearchOverlayController'; import SearchIcon from '@semo-client/ui/assets/icons/Icon'; -import searchPalette from '@semo-client/ui/styles/pallete/searchPalette.ts'; interface SearchInputProps { query: string; @@ -20,51 +20,21 @@ export const SearchInput: React.FC = ({ onSubmit, results, }) => { - const [isExpanded, setIsExpanded] = useState(false); - const [isOverlayVisible, setIsOverlayVisible] = useState(false); - - const handleExpand = () => { - setIsExpanded(true); - setIsOverlayVisible(true); - }; - - const handleCollapse = () => { - setIsExpanded(false); - }; - - const handleKeyPress = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - onSubmit(); - handleCollapse(); - } - }; - - useEffect(() => { - if (!isExpanded) { - // 오버레이를 애니메이션 후에 숨김 - const timeoutId = setTimeout(() => { - setIsOverlayVisible(false); - }, 300); // 트랜지션 시간과 동일하게 설정 - - // 검색창 초기화 - setQuery(''); - - return () => clearTimeout(timeoutId); - } else { - setIsOverlayVisible(true); - } - }, [isExpanded]); + const { + isExpanded, + isOverlayVisible, + handleExpand, + handleCollapse, + handleKeyPress, + handleOverlayAnimationEnd, + } = useSearchOverlayController({ onSubmit, setQuery }); return ( <> - + = ({ }} isExpanded={isExpanded} autoFocus={isExpanded} + aria-label='검색 입력창' /> - {results.length != 0 && ( + {isExpanded && results.length > 0 && ( )} {isOverlayVisible && ( - + )} ); }; +const fadeIn = keyframes` + from { + opacity: 0; + } + to { + opacity: 1; + } +`; + +const fadeOut = keyframes` + from { + opacity: 1; + } + to { + opacity: 0; + } +`; + const SearchInputWrapper = styled.div<{ isExpanded: boolean }>` position: absolute; top: ${({ isExpanded }) => (isExpanded ? '40%' : '65%')}; @@ -107,11 +101,12 @@ const InputContainer = styled.div<{ isExpanded: boolean }>` align-items: center; width: 100%; height: 54px; - border-radius: ${({ isExpanded }) => - isExpanded ? '8px 8px 0 0' : '8px'}; /* 조건부 border-radius */ - background-color: ${({ isExpanded }) => - isExpanded ? searchPalette.blackGray[90] : searchPalette.blackGray[70]}; - border: 1px solid ${searchPalette.whiteGray[30]}; + border-radius: ${({ isExpanded }) => (isExpanded ? '8px 8px 0 0' : '8px')}; + background-color: ${({ isExpanded, theme }) => + isExpanded + ? theme.searchPalette.blackGray[90] + : theme.searchPalette.blackGray[70]}; + border: 1px solid ${({ theme }) => theme.searchPalette.whiteGray[30]}; padding: 0 14px; gap: 10px; font-size: 20px; @@ -119,12 +114,11 @@ const InputContainer = styled.div<{ isExpanded: boolean }>` &:focus-within { outline: none; - border-color: ${searchPalette.whiteGray[50]}; - background: ${({ isExpanded }) => - isExpanded ? searchPalette.whiteGray[70] : '#000000'}; + border-color: ${({ theme }) => theme.searchPalette.whiteGray[50]}; + background: ${({ isExpanded, theme }) => + isExpanded ? theme.searchPalette.whiteGray[70] : '#000000'}; } - /* 애니메이션 효과 */ transition: all 0.3s ease-in-out; `; @@ -140,15 +134,17 @@ const StyledInput = styled.input<{ isExpanded: boolean }>` height: 100%; border: none; background: transparent; - color: ${({ isExpanded }) => - isExpanded ? searchPalette.black : searchPalette.white}; + color: ${({ isExpanded, theme }) => + isExpanded ? theme.searchPalette.black : theme.searchPalette.white}; font-size: 20px; position: relative; z-index: 2; &::placeholder { - color: ${({ isExpanded }) => - isExpanded ? '#000' : searchPalette.whiteGray[50]}; + color: ${({ isExpanded, theme }) => + isExpanded + ? theme.searchPalette.black + : theme.searchPalette.whiteGray[50]}; } &:focus { @@ -162,8 +158,9 @@ const Overlay = styled.div<{ isExpanded: boolean }>` left: 0; width: 100%; height: 100%; - background-color: ${searchPalette.blackGray[90]}; + background-color: ${({ theme }) => theme.searchPalette.blackGray[90]}; z-index: 999; - opacity: ${({ isExpanded }) => (isExpanded ? 1 : 0)}; - transition: opacity 0.3s ease-in-out; + animation: ${({ isExpanded }) => (isExpanded ? fadeIn : fadeOut)} 0.3s + forwards; + pointer-events: ${({ isExpanded }) => (isExpanded ? 'auto' : 'none')}; `; diff --git a/src/features/search/ui/components/SearchResults.tsx b/src/features/search/ui/components/SearchResults.tsx index 407cbe6..0fddd48 100644 --- a/src/features/search/ui/components/SearchResults.tsx +++ b/src/features/search/ui/components/SearchResults.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import styled from 'styled-components'; -import { Professor } from '@semo-client/features/search/models/Professor.ts'; -import searchPalette from '@semo-client/ui/styles/pallete/searchPalette.ts'; +import styled, { keyframes } from 'styled-components'; +import { Professor } from '@semo-client/features/search/models/Professor'; interface SearchResultsProps { value: string; @@ -17,8 +16,8 @@ export const SearchResults: React.FC = ({ {results.map((professor, index) => ( - {/*// TODO: 이미지 추가 필요*/} - {/**/} + {/* TODO: 이미지 추가 필요 */} + {/* */} {professor.name} 교수님 @@ -44,24 +43,54 @@ export const SearchResults: React.FC = ({ ); }; +// 애니메이션 정의 (필요 시) +const fadeIn = keyframes` + from { + opacity: 0; + } + to { + opacity: 1; + } +`; + +const fadeOut = keyframes` + from { + opacity: 1; + } + to { + opacity: 0; + } +`; + const ResultsContainer = styled.div<{ value: string }>` position: absolute; + z-index: 999; width: 100%; - background: ${searchPalette.whiteGray[70]}; + background: ${({ theme }) => theme.searchPalette.whiteGray[70]}; border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; opacity: ${({ value }) => (value ? 1 : 0)}; padding: 14px; + transition: opacity 0.3s ease-in-out; `; const ResultItem = styled.div` - background-color: ${searchPalette.blackGray[10]}; + background-color: ${({ theme }) => theme.searchPalette.blackGray[10]}; backdrop-filter: blur(1px); - border: 2px solid ${searchPalette.blackGray[10]}; + border: 2px solid ${({ theme }) => theme.searchPalette.blackGray[10]}; padding: 12px; border-radius: 8px; margin-bottom: 10px; color: #fff; + cursor: pointer; + + &:last-child { + margin-bottom: 0; + } + + &:hover { + background-color: ${({ theme }) => theme.searchPalette.blackGray[20]}; + } `; const Profile = styled.div` @@ -84,20 +113,20 @@ const ProfileImage = styled.span` const Name = styled.p` font-size: 16px; font-weight: 700; - color: ${searchPalette.black}; + color: ${({ theme }) => theme.searchPalette.black}; `; const Department = styled.p` font-size: 14px; font-weight: 400; - color: ${searchPalette.black}; + color: ${({ theme }) => theme.searchPalette.black}; `; const Hr = styled.hr` width: 100%; margin-top: 10px; margin-bottom: 10px; - border: 1px solid ${searchPalette.blackGray[30]}; + border: 1px solid ${({ theme }) => theme.searchPalette.blackGray[30]}; `; const Details = styled.div` @@ -105,11 +134,13 @@ const Details = styled.div` align-items: center; gap: 6px; margin-bottom: 8px; + p { - color: ${searchPalette.black}; + color: ${({ theme }) => theme.searchPalette.black}; font-size: 16px; font-weight: 400; } + &:last-child { margin-bottom: 0; } diff --git a/src/main.tsx b/src/main.tsx index 8d46db3..fe707b1 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,6 @@ import ReactDOM from 'react-dom/client'; import React from 'react'; -import App from './App.tsx'; +import App from './App'; ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 08d1b87..5f9b2d4 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -6,12 +6,11 @@ import { Clock } from '@semo-client/features/clock/ui/components/Clock'; import { HeaderView } from '@semo-client/features/header/ui/components/HeaderView'; import { NoticeView } from '@semo-client/features/notice/ui/components/NoticeView'; import professorsData from '@semo-client/features/search/data/professors.json'; -import { Professor } from '@semo-client/features/search/models/Professor.ts'; +import { Professor } from '@semo-client/features/search/models/Professor'; import { SearchInput } from '@semo-client/features/search/ui/components/SearchInput'; import { TrendingKeywords } from '@semo-client/features/search/ui/components/TrendingKeywords'; import { LoginButtonView } from '@semo-client/features/users/ui/components/LoginButtonView'; import { getInitials } from '@semo-utils/search/hangulUtils'; -import { Controls } from '@storybook/blocks'; /** * TODO 각 요소 컴포넌트 에는 추가 스타일(여백,마진) 들어있지 않은 순수 요소 컴포넌트 From a28220e676e5b3250d500d7ab6ddfe4a9e46293a Mon Sep 17 00:00:00 2001 From: anhye0n Date: Wed, 13 Nov 2024 17:49:24 +0900 Subject: [PATCH 16/17] =?UTF-8?q?feat:=20site=20result=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 +- src/features/search/data/total.json | 95 ++++++++ src/features/search/logo/logo_0.svg | 65 +++++ src/features/search/logo/logo_1.svg | 9 + src/features/search/models/models.ts | 20 ++ .../search/ui/components/SearchInput.tsx | 4 +- .../search/ui/components/SearchResults.tsx | 226 +++++++++++++----- src/pages/Home.tsx | 49 +++- 8 files changed, 401 insertions(+), 69 deletions(-) create mode 100644 src/features/search/data/total.json create mode 100755 src/features/search/logo/logo_0.svg create mode 100755 src/features/search/logo/logo_1.svg create mode 100644 src/features/search/models/models.ts diff --git a/src/App.tsx b/src/App.tsx index 20ab36c..fde423f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,8 +9,8 @@ function App() { return ( - + diff --git a/src/features/search/data/total.json b/src/features/search/data/total.json new file mode 100644 index 0000000..be5f1a1 --- /dev/null +++ b/src/features/search/data/total.json @@ -0,0 +1,95 @@ +{ + "professor": [ + { + "id": 0, + "name": "권순일", + "labLocation": "대양AI 624호", + "phoneNumber": "02-3408-3847", + "email": "sikwon@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "id": 1, + "name": "백성욱", + "labLocation": "대양AI 622호", + "phoneNumber": "02-3408-3797", + "email": "sbaik@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "id": 2, + "name": "이종원", + "labLocation": "대양AI 619호", + "phoneNumber": "02-3408-3798", + "email": "jwlee@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "id": 3, + "name": "송오영", + "labLocation": "대양AI 625호", + "phoneNumber": "02-3408-3830", + "email": "oysong@sejong.edu", + "department": "소프트웨어학과" + }, + { + "id": 4, + "name": "최준연", + "labLocation": "대양AI 620호", + "phoneNumber": "02-3408-3887", + "email": "zoon@sejong.edu", + "department": "소프트웨어학과" + }, + { + "id": 5, + "name": "박상일", + "labLocation": "대양AI 626호", + "phoneNumber": "02-3408-3832", + "email": "sipark@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "id": 6, + "name": "변재욱", + "labLocation": "대양AI 604호", + "phoneNumber": "02-3408-1847", + "email": "jwbyun@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "id": 7, + "name": "이은상", + "labLocation": "대양AI 621호", + "phoneNumber": "02-3408-2975", + "email": "eslee3209@sejong.ac.kr", + "department": "소프트웨어학과" + }, + { + "id": 8, + "name": "정승화", + "labLocation": "대양AI 623호", + "phoneNumber": "02-3408-3795", + "email": "seunghwajeong@sejong.ac.kr", + "department": "소프트웨어학과" + } + ], + "sites": [ + { + "id": 0, + "type": 1, + "name": "학사정보시스템", + "description": "", + "url": "https://sjpt.sejong.ac.kr", + "keywords" : ["수강신청", "등록금"] + }, + + { + "id": 1, + "type": 0, + "name": "Tutorial Sejong", + "description": "수강신청 연습 사이트", + "url": "https://tutorial-sejong.com", + "keywords" : ["수강신청", "연습"] + } + ] +} \ No newline at end of file diff --git a/src/features/search/logo/logo_0.svg b/src/features/search/logo/logo_0.svg new file mode 100755 index 0000000..3f41e2d --- /dev/null +++ b/src/features/search/logo/logo_0.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/features/search/logo/logo_1.svg b/src/features/search/logo/logo_1.svg new file mode 100755 index 0000000..5265876 --- /dev/null +++ b/src/features/search/logo/logo_1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/features/search/models/models.ts b/src/features/search/models/models.ts new file mode 100644 index 0000000..9c78280 --- /dev/null +++ b/src/features/search/models/models.ts @@ -0,0 +1,20 @@ +export interface Professor { + id: number; + name: string; + labLocation: string; + phoneNumber: string; + email: string; + department: string; +} + +export interface Site { + id: number; + type: number; + name: string; + description: string; + url: string; + keywords: string[]; +} + +// 검색 결과를 위한 유니온 타입 +export type SearchResultTypes = Professor | Site; diff --git a/src/features/search/ui/components/SearchInput.tsx b/src/features/search/ui/components/SearchInput.tsx index 85a72ae..65ee5c1 100644 --- a/src/features/search/ui/components/SearchInput.tsx +++ b/src/features/search/ui/components/SearchInput.tsx @@ -1,6 +1,6 @@ import React from 'react'; import styled, { keyframes } from 'styled-components'; -import { Professor } from '@semo-client/features/search/models/Professor'; +import { SearchResultTypes } from '@semo-client/features/search/models/models'; import { SearchResults } from '@semo-client/features/search/ui/components/SearchResults'; import { useSearchOverlayController } from '@semo-client/features/search/ui/hooks/useSearchOverlayController'; import SearchIcon from '@semo-client/ui/assets/icons/Icon'; @@ -10,7 +10,7 @@ interface SearchInputProps { setQuery: (query: string) => void; onChange: (e: React.ChangeEvent) => void; onSubmit: () => void; - results: Professor[]; + results: SearchResultTypes[]; } export const SearchInput: React.FC = ({ diff --git a/src/features/search/ui/components/SearchResults.tsx b/src/features/search/ui/components/SearchResults.tsx index 0fddd48..e1f95a3 100644 --- a/src/features/search/ui/components/SearchResults.tsx +++ b/src/features/search/ui/components/SearchResults.tsx @@ -1,80 +1,135 @@ +// src/components/SearchResults.tsx import React from 'react'; -import styled, { keyframes } from 'styled-components'; -import { Professor } from '@semo-client/features/search/models/Professor'; +import styled from 'styled-components'; +// SVG 로고 이미지 임포트 +import logo_0 from '@semo-client/features/search/logo/logo_0.svg'; +import logo_1 from '@semo-client/features/search/logo/logo_1.svg'; +import { + SearchResultTypes, + Professor, + Site, +} from '@semo-client/features/search/models/models'; interface SearchResultsProps { value: string; - results: Professor[]; + results: SearchResultTypes[]; } +const logoMap: { [key: number] } = { + 0: logo_0, + 1: logo_1, +}; + export const SearchResults: React.FC = ({ value, results, }) => { return ( - - {results.map((professor, index) => ( - - - {/* TODO: 이미지 추가 필요 */} - {/* */} - - - {professor.name} 교수님 - {professor.department} - - -
-
- 연구실 -

{professor.labLocation}

-
-
- 전화번호 -

{professor.phoneNumber}

-
-
- 이메일 -

{professor.email}

-
+ + {results.map(result => ( + + {isProfessor(result) ? ( + + + {/* TODO: 이미지 추가 필요 */} + {/* */} + + + {result.name} 교수님 + {result.department} + + +
+
+ 연구실 +

{result.labLocation}

+
+
+ 전화번호 +

{result.phoneNumber}

+
+
+ 이메일 +

{result.email}

+
+
+ ) : ( + + + {/* 로고 이미지 동적 렌더링 */} + {result.id !== undefined && logoMap[result.id] ? ( + + ) : ( + + )} + + + {result.name} + {result.description && ( + + {' — '} + {result.description} + + )} + + {result.url} + + + window.open(result.url)}> + ↵으로 이동 + + + )}
))}
); }; -// 애니메이션 정의 (필요 시) -const fadeIn = keyframes` - from { - opacity: 0; - } - to { - opacity: 1; - } -`; - -const fadeOut = keyframes` - from { - opacity: 1; - } - to { - opacity: 0; - } -`; +// 타입 가드 함수 +const isProfessor = (result: SearchResultTypes): result is Professor => { + return (result as Professor).department !== undefined; +}; -const ResultsContainer = styled.div<{ value: string }>` +// 스타일 정의 +const ResultsContainer = styled.div` position: absolute; z-index: 999; width: 100%; background: ${({ theme }) => theme.searchPalette.whiteGray[70]}; border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; - opacity: ${({ value }) => (value ? 1 : 0)}; padding: 14px; - transition: opacity 0.3s ease-in-out; + max-height: 400px; + overflow-y: auto; +`; + +const HoverBadge = styled.span` + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + color: #fff; + border-radius: 4px; + opacity: 0; + visibility: hidden; + display: flex; + padding: 2px 6px; + justify-content: center; + align-items: center; + font-size: 14px; + background: #818181; `; const ResultItem = styled.div` + position: relative; /* HoverBadge의 절대 위치를 위해 추가 */ background-color: ${({ theme }) => theme.searchPalette.blackGray[10]}; backdrop-filter: blur(1px); border: 2px solid ${({ theme }) => theme.searchPalette.blackGray[10]}; @@ -90,11 +145,36 @@ const ResultItem = styled.div` &:hover { background-color: ${({ theme }) => theme.searchPalette.blackGray[20]}; + + /* HoverBadge 보이기 */ + + ${HoverBadge} { + opacity: 1; + visibility: visible; + } + } +`; + +const ProfessorResult = styled.div` + /* 교수 결과에 대한 추가 스타일 */ +`; + +const SiteResult = styled.div` + /* 사이트 결과에 대한 추가 스타일 */ + + a { + color: #007bff; + text-decoration: none; + + &:hover { + text-decoration: underline; + } } `; const Profile = styled.div` display: flex; + align-items: center; `; const ProfileInfo = styled.div` @@ -110,22 +190,59 @@ const ProfileImage = styled.span` height: 36px; `; +const SiteIcon = styled.img` + background: #ffffff; + border-radius: 8px; + width: 36px; + height: 36px; + padding: 3px; +`; + +const DefaultSiteIcon = styled.span` + background: #007bff; + border-radius: 50%; + width: 36px; + height: 36px; +`; + const Name = styled.p` font-size: 16px; - font-weight: 700; + font-weight: 600; color: ${({ theme }) => theme.searchPalette.black}; + margin: 0; `; const Department = styled.p` font-size: 14px; font-weight: 400; color: ${({ theme }) => theme.searchPalette.black}; + margin: 0; +`; + +const NameDescriptionBox = styled.div` + display: flex; + align-items: center; + justify-content: left; +`; + +const Description = styled.p` + margin-top: 2px; + margin-left: 10px; + color: #000000; + font-size: 12px; + font-weight: 400; +`; + +const URL = styled.p` + font-size: 12px; + font-weight: 300; + color: ${({ theme }) => theme.searchPalette.black}; + margin: 0; `; const Hr = styled.hr` width: 100%; - margin-top: 10px; - margin-bottom: 10px; + margin: 10px 0; border: 1px solid ${({ theme }) => theme.searchPalette.blackGray[30]}; `; @@ -139,6 +256,7 @@ const Details = styled.div` color: ${({ theme }) => theme.searchPalette.black}; font-size: 16px; font-weight: 400; + margin: 0; } &:last-child { @@ -148,11 +266,11 @@ const Details = styled.div` const Badge = styled.span` display: flex; - padding: 2px 3px; + padding: 2px 6px; justify-content: center; align-items: center; - gap: 10px; border-radius: 4px; font-size: 14px; background: #818181; + color: #fff; `; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 5f9b2d4..0a47949 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -5,8 +5,12 @@ import { BackgroundImageView } from '@semo-client/features/background-image/ui/c import { Clock } from '@semo-client/features/clock/ui/components/Clock'; import { HeaderView } from '@semo-client/features/header/ui/components/HeaderView'; import { NoticeView } from '@semo-client/features/notice/ui/components/NoticeView'; -import professorsData from '@semo-client/features/search/data/professors.json'; -import { Professor } from '@semo-client/features/search/models/Professor'; +import totalData from '@semo-client/features/search/data/total.json'; +import { + Professor, + Site, + SearchResultTypes, +} from '@semo-client/features/search/models/models'; import { SearchInput } from '@semo-client/features/search/ui/components/SearchInput'; import { TrendingKeywords } from '@semo-client/features/search/ui/components/TrendingKeywords'; import { LoginButtonView } from '@semo-client/features/users/ui/components/LoginButtonView'; @@ -18,12 +22,12 @@ import { getInitials } from '@semo-utils/search/hangulUtils'; */ export const Home = () => { const [query, setQuery] = useState(''); - // 검색 결과 반영 - const [results, setResults] = useState([]); + // 검색 결과 반영 (교수 및 사이트 포함) + const [results, setResults] = useState([]); /** - * 검색어에 따라 교수님 데이터를 필터링합니다. - * 전체 이름, 초성, 일부 이름을 포함하는지 확인합니다. + * 검색어에 따라 교수 및 사이트 데이터를 필터링합니다. + * 전체 이름, 초성, 일부 이름 또는 사이트 이름 및 키워드를 포함하는지 확인합니다. * @param searchQuery 사용자 입력 검색어 */ const fetchData = (searchQuery: string) => { @@ -35,17 +39,37 @@ export const Home = () => { const normalizedQuery = searchQuery.trim().toLowerCase(); const queryInitials = getInitials(normalizedQuery); - const filtered = professorsData.filter(professor => { - const name = professor.name.toLowerCase(); - const initials = getInitials(professor.name.toLowerCase()); + // 교수 검색 + const filteredProfessors: Professor[] = totalData.professor.filter( + professor => { + const name = professor.name.toLowerCase(); + const initials = getInitials(name); + + return ( + name.includes(normalizedQuery) || // 전체 이름 또는 일부 이름 포함 + initials.includes(normalizedQuery) // 초성 포함 + ); + }, + ); + + // 사이트 검색 + const filteredSites: Site[] = totalData.sites.filter(site => { + const name = site.name.toLowerCase(); + const keywords = site.keywords.map(keyword => keyword.toLowerCase()); return ( - name.includes(normalizedQuery) || // 전체 이름 또는 일부 이름 포함 - initials.includes(normalizedQuery) // 초성 포함 + name.includes(normalizedQuery) || // 사이트 이름 포함 + keywords.some(keyword => keyword.includes(normalizedQuery)) // 키워드 포함 ); }); - setResults(filtered); + // 결과 합치기 + const combinedResults: SearchResult[] = [ + ...filteredProfessors, + ...filteredSites, + ]; + + setResults(combinedResults); }; const debouncedSearch = useCallback( @@ -65,6 +89,7 @@ export const Home = () => { debouncedSearch.cancel(); fetchData(query); }; + return ( From cb255ec20a8b007d247a21e9ffafe1e87c917b56 Mon Sep 17 00:00:00 2001 From: anhye0n Date: Wed, 27 Nov 2024 17:34:08 +0900 Subject: [PATCH 17/17] =?UTF-8?q?feat:=20logo=20=ED=8C=8C=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../icons/Google_Chrome_icon_(February_2022).svg | 2 ++ src/ui/assets/icons/Prototype.zip | Bin 0 -> 615 bytes src/ui/assets/icons/copy.svg | 9 +++++++++ src/ui/assets/icons/naver.svg | 2 ++ ...ast_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg | 1 + 5 files changed, 14 insertions(+) create mode 100644 src/ui/assets/icons/Google_Chrome_icon_(February_2022).svg create mode 100644 src/ui/assets/icons/Prototype.zip create mode 100755 src/ui/assets/icons/copy.svg create mode 100644 src/ui/assets/icons/naver.svg create mode 100644 src/ui/assets/icons/north_east_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg diff --git a/src/ui/assets/icons/Google_Chrome_icon_(February_2022).svg b/src/ui/assets/icons/Google_Chrome_icon_(February_2022).svg new file mode 100644 index 0000000..3ffa2aa --- /dev/null +++ b/src/ui/assets/icons/Google_Chrome_icon_(February_2022).svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/ui/assets/icons/Prototype.zip b/src/ui/assets/icons/Prototype.zip new file mode 100644 index 0000000000000000000000000000000000000000..0327674f5c65dbda73090b0508ae1a534217468e GIT binary patch literal 615 zcmWIWW@h1HU|`^2FzzmiG*}lq`!XW~!#-vP21%f3Kv8~4eo19Ps(yg8zFSdZZmNQj zpMOc0bjYI|W_}$-VnyM_vBvpYMK54ED1S>}t7De7n^0ZGWxn z$>Z7A{Vj9%$gi86GB4fCyG-V_%F{XO4nFzk^NziG@L$z{sl07Y{jN_RgbrRU)Zkxh z-)MB%gmK#SqAJC=!G4FE{)@@TC2P$5vvPIPms9iGUhR6*wrq(Ci`=hk&3m`6l&WWp z|GK$;M$_v#US58ucl_EEzwOPlpY^}PZa=q=*&#mNb3*IRxG#=XZ}iUnFRGDz(SFIC zC2Obo5hM2LnkJhkoD~p0+fx=PwISpf)3uetiupT*guZ#F_cG)dtu#Jyx`6T80pUNpaX6f-3VH?7N5@fVp<=(KoN?3k@txPC4X}RpWvS%OT z);?*{uhkL>KK)FT-FPLV;Q`Hp#_;8DCy5zd-Y{31H>$zXyiX%wv$tv1TK7Y + + + + + + + + diff --git a/src/ui/assets/icons/naver.svg b/src/ui/assets/icons/naver.svg new file mode 100644 index 0000000..6a36296 --- /dev/null +++ b/src/ui/assets/icons/naver.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/ui/assets/icons/north_east_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg b/src/ui/assets/icons/north_east_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg new file mode 100644 index 0000000..85163c5 --- /dev/null +++ b/src/ui/assets/icons/north_east_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg @@ -0,0 +1 @@ + \ No newline at end of file