@@ -14,51 +14,103 @@ const CardFront = ({ displayedUser, currentUser }: CardProps) => {
1414 const containerRef = useRef < HTMLDivElement > ( null ) ;
1515 const interestsContainerRef = useRef < HTMLDivElement > ( null ) ;
1616 const coursesContainerRef = useRef < HTMLDivElement > ( null ) ;
17- const [ isHiddenInterestExist , setHiddenInterestExist ] = useState ( false ) ;
18- const [ isHiddenCourseExist , setHiddenCourseExist ] = useState ( false ) ;
1917
2018 const calculateVisibleCourses = useCallback ( ( ) => {
2119 const courses = displayedUser . courses ;
2220 const container = coursesContainerRef . current ;
2321 if ( ! container ) return ;
2422
25- const containerHeight = container . offsetHeight ; // コンテナの高さを取得
23+ const containerHeight = container . offsetHeight ;
2624
27- // 一旦コンテナを初期化
25+ // 初期化
2826 container . innerHTML = "" ;
29- setHiddenCourseExist ( false ) ;
3027
31- // courses を一致・非一致で分類
28+ const coursesContainer = document . createElement ( "div" ) ;
29+ coursesContainer . classList . add (
30+ "flex" ,
31+ "flex-wrap" ,
32+ "gap-3" ,
33+ "justify-start" ,
34+ ) ;
35+ container . appendChild ( coursesContainer ) ;
36+
37+ // `And More` 要素を作成して追加 (最初は非表示)
38+ const andMoreElement = document . createElement ( "p" ) ;
39+ andMoreElement . textContent = "And More" ;
40+ andMoreElement . classList . add (
41+ "text-sm" ,
42+ "text-gray-500" ,
43+ "text-center" ,
44+ "mt-2" ,
45+ "hidden" , // 初期状態で非表示
46+ ) ;
47+ andMoreElement . style . width = "100%" ;
48+ coursesContainer . appendChild ( andMoreElement ) ;
49+
50+ // 一致しているコースと一致していないコースを分ける
3251 const matchingCourses = courses . filter ( ( course ) =>
3352 currentUser . courses . some ( ( c ) => c . id === course . id ) ,
3453 ) ;
3554 const nonMatchingCourses = courses . filter (
3655 ( course ) => ! currentUser . courses . some ( ( c ) => c . id === course . id ) ,
3756 ) ;
3857
39- // courses を表示する flex コンテナ
40- const coursesContainer = document . createElement ( "div" ) ;
41- coursesContainer . classList . add ( "flex" , "flex-wrap" , "gap-2" ) ;
42- container . appendChild ( coursesContainer ) ;
43-
44- // 一致しているコースを先に表示
58+ // バッジの生成
59+ const addedElements : HTMLElement [ ] = [ ] ;
4560 for ( const course of [ ...matchingCourses , ...nonMatchingCourses ] ) {
4661 const isMatching = currentUser . courses . some ( ( c ) => c . id === course . id ) ;
4762
48- // 新しい div 要素を作成
4963 const element = document . createElement ( "div" ) ;
5064 element . textContent = course . name ;
5165
52- // スタイル適用(赤 or 灰色)
53- element . classList . add ( "badge" , "badge-outline" ) ;
54- element . style . backgroundColor = isMatching ? "red" : "gray" ;
66+ element . classList . add (
67+ "rounded-full" ,
68+ "text-center" ,
69+ "px-4" ,
70+ "py-2" ,
71+ "text-base" ,
72+ isMatching ? "font-bold" : "font-normal" ,
73+ ) ;
74+ element . style . backgroundColor = "gray" ;
5575 element . style . color = "white" ;
76+ element . style . flexShrink = "0" ;
77+
78+ coursesContainer . insertBefore ( element , andMoreElement ) ;
79+ addedElements . push ( element ) ;
80+
81+ // バッジがはみ出す場合は削除
82+ if (
83+ coursesContainer . offsetHeight > containerHeight ||
84+ andMoreElement . offsetHeight + coursesContainer . offsetHeight >
85+ containerHeight
86+ ) {
87+ coursesContainer . removeChild ( element ) ;
88+ addedElements . pop ( ) ;
89+ break ;
90+ }
91+ }
5692
57- // 表示判定
58- if ( coursesContainer . offsetHeight + 30 <= containerHeight ) {
59- coursesContainer . appendChild ( element ) ;
93+ // すべてのバッジが表示されている場合は `And More` を非表示
94+ if (
95+ addedElements . length ===
96+ matchingCourses . length + nonMatchingCourses . length
97+ ) {
98+ andMoreElement . classList . add ( "hidden" ) ;
99+ } else {
100+ andMoreElement . classList . remove ( "hidden" ) ;
101+ }
102+
103+ // ループ後、`And More` が完全に表示されるか確認
104+ while (
105+ coursesContainer . offsetHeight > containerHeight ||
106+ andMoreElement . offsetHeight + coursesContainer . offsetHeight >
107+ containerHeight
108+ ) {
109+ const lastElement = addedElements . pop ( ) ;
110+ if ( lastElement ) {
111+ coursesContainer . removeChild ( lastElement ) ;
60112 } else {
61- setHiddenCourseExist ( true ) ;
113+ break ; // バッジがなくなる場合は終了
62114 }
63115 }
64116 } , [ displayedUser , currentUser ] ) ;
@@ -68,13 +120,29 @@ const CardFront = ({ displayedUser, currentUser }: CardProps) => {
68120 const container = interestsContainerRef . current ;
69121 if ( ! container ) return ;
70122
71- const containerHeight = container . offsetHeight ; // コンテナの高さを取得
123+ const containerHeight = container . offsetHeight ;
72124
73- // 一旦コンテナを初期化
125+ // 初期化
74126 container . innerHTML = "" ;
75- setHiddenInterestExist ( false ) ;
76127
77- // interests を一致・非一致で分類
128+ const flexContainer = document . createElement ( "div" ) ;
129+ flexContainer . classList . add ( "flex" , "flex-wrap" , "gap-3" , "justify-start" ) ;
130+ container . appendChild ( flexContainer ) ;
131+
132+ // `And More` 要素を作成して追加 (最初は非表示)
133+ const andMoreElement = document . createElement ( "p" ) ;
134+ andMoreElement . textContent = "And More" ;
135+ andMoreElement . classList . add (
136+ "text-sm" ,
137+ "text-gray-500" ,
138+ "text-center" ,
139+ "mt-2" ,
140+ "hidden" , // 初期状態で非表示
141+ ) ;
142+ andMoreElement . style . width = "100%" ;
143+ flexContainer . appendChild ( andMoreElement ) ;
144+
145+ // 一致している興味分野と一致していない興味分野を分ける
78146 const matchingInterests = interests . filter ( ( interest ) =>
79147 currentUser . interestSubjects . some ( ( i ) => i . name === interest . name ) ,
80148 ) ;
@@ -83,34 +151,63 @@ const CardFront = ({ displayedUser, currentUser }: CardProps) => {
83151 ! currentUser . interestSubjects . some ( ( i ) => i . name === interest . name ) ,
84152 ) ;
85153
86- // interests を表示する flex コンテナ
87- const flexContainer = document . createElement ( "div" ) ;
88- flexContainer . classList . add ( "flex" , "flex-wrap" , "gap-2" ) ;
89- container . appendChild ( flexContainer ) ;
90-
91- // 一致している興味分野を先に表示
154+ // バッジの生成
155+ const addedElements : HTMLElement [ ] = [ ] ;
92156 for ( const interest of [ ...matchingInterests , ...nonMatchingInterests ] ) {
93157 const isMatching = currentUser . interestSubjects . some (
94158 ( i ) => i . name === interest . name ,
95159 ) ;
96160
97- // 新しい div 要素を作成
98161 const element = document . createElement ( "div" ) ;
99162 element . textContent = interest . name ;
100163
101- // スタイル適用(赤 or 灰色)
102- element . classList . add ( "badge" , "badge-outline" ) ;
103- element . style . backgroundColor = isMatching ? "red" : "gray" ;
104- element . style . color = "white" ;
105- element . style . overflow = "hidden" ;
106- element . style . whiteSpace = "nowrap" ;
107- element . style . textOverflow = "ellipsis" ;
164+ element . classList . add (
165+ "rounded-full" ,
166+ "text-center" ,
167+ "px-4" ,
168+ "py-2" ,
169+ "text-base" ,
170+ isMatching ? "font-bold" : "font-normal" ,
171+ ) ;
172+ element . style . backgroundColor = "#FFF1BF" ;
173+ element . style . color = "#039BE5" ;
174+ element . style . flexShrink = "0" ;
175+
176+ flexContainer . insertBefore ( element , andMoreElement ) ;
177+ addedElements . push ( element ) ;
178+
179+ // バッジがはみ出す場合は削除
180+ if (
181+ flexContainer . offsetHeight > containerHeight ||
182+ andMoreElement . offsetHeight + flexContainer . offsetHeight >
183+ containerHeight
184+ ) {
185+ flexContainer . removeChild ( element ) ;
186+ addedElements . pop ( ) ;
187+ break ;
188+ }
189+ }
190+
191+ // すべてのバッジが表示されている場合は `And More` を非表示
192+ if (
193+ addedElements . length ===
194+ matchingInterests . length + nonMatchingInterests . length
195+ ) {
196+ andMoreElement . classList . add ( "hidden" ) ;
197+ } else {
198+ andMoreElement . classList . remove ( "hidden" ) ;
199+ }
108200
109- // 表示判定
110- if ( flexContainer . offsetHeight + 30 <= containerHeight ) {
111- flexContainer . appendChild ( element ) ;
201+ // ループ後、`And More` が完全に表示されるか確認
202+ while (
203+ flexContainer . offsetHeight > containerHeight ||
204+ andMoreElement . offsetHeight + flexContainer . offsetHeight > containerHeight
205+ ) {
206+ const lastElement = addedElements . pop ( ) ;
207+ if ( lastElement ) {
208+ flexContainer . removeChild ( lastElement ) ;
112209 } else {
113- setHiddenInterestExist ( true ) ;
210+ break ; // バッジがなくなる場合は終了
114211 }
115212 }
116213 } , [ displayedUser , currentUser ] ) ;
@@ -126,7 +223,6 @@ const CardFront = ({ displayedUser, currentUser }: CardProps) => {
126223
127224 resizeObserver . observe ( container ) ;
128225
129- // 初期計算を実行
130226 calculateVisibleInterests ( ) ;
131227 calculateVisibleCourses ( ) ;
132228
@@ -149,29 +245,21 @@ const CardFront = ({ displayedUser, currentUser }: CardProps) => {
149245 </ div >
150246 </ div >
151247
248+ < p className = "text-center font-bold text-lg" > 履修している科目</ p >
152249 < div className = "flex h-[70%] w-full flex-col gap-2" ref = { containerRef } >
153250 < div
154- ref = { interestsContainerRef }
251+ ref = { coursesContainerRef }
155252 className = "width-full h-[50%] overflow-hidden"
156253 >
157254 < div />
158- { isHiddenInterestExist && (
159- < div className = "badge badge-outline bg-gray-200 text-gray-700" >
160- And More
161- </ div >
162- ) }
163255 </ div >
164256
257+ < p className = "text-center font-bold text-lg" > 興味のある分野</ p >
165258 < div
166- ref = { coursesContainerRef }
259+ ref = { interestsContainerRef }
167260 className = "width-full h-[50%] overflow-hidden"
168261 >
169262 < div />
170- { isHiddenCourseExist && (
171- < div className = "badge badge-outline bg-gray-200 text-gray-700" >
172- And More
173- </ div >
174- ) }
175263 </ div >
176264 </ div >
177265 </ div >
0 commit comments