Skip to content

Commit 992133f

Browse files
committed
カードの表面のデザイン&表示ロジック修正
1 parent 69ce1c0 commit 992133f

File tree

1 file changed

+143
-55
lines changed

1 file changed

+143
-55
lines changed

web/components/Card.tsx

Lines changed: 143 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)