Skip to content

Commit 8fbb585

Browse files
committed
Restore expandable card descriptions while keeping uniform matchmaking card heights.
Re-enable Show more behavior for card text and use flexible card sizing so CTAs stay visible without uneven grid layouts. Made-with: Cursor
1 parent c53dc7b commit 8fbb585

1 file changed

Lines changed: 84 additions & 4 deletions

File tree

resources/js/components/matchmaking/ToolCard.vue

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="flex h-[430px] flex-col bg-white rounded-lg overflow-hidden">
2+
<div class="flex h-full min-h-[430px] flex-col bg-white rounded-lg overflow-hidden">
33
<div
44
class="flex-shrink-0 flex justify-center items-center w-full h-[178px] bg-white"
55
>
@@ -58,10 +58,27 @@
5858

5959
<div
6060
v-if="tool.description"
61-
class="text-slate-500 text-[16px] leading-[22px] mb-2 overflow-hidden"
62-
style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5"
61+
ref="descriptionContainerRef"
62+
class="flex-grow min-h-0"
63+
:class="{ 'overflow-hidden': needShowMore && !showMore }"
6364
>
64-
{{ tool.description }}
65+
<div
66+
ref="descriptionRef"
67+
class="relative flex-grow text-slate-500 text-[16px] leading-[22px] mb-2 overflow-hidden"
68+
style="height: auto"
69+
>
70+
<div v-html="formatMultiline(tool.description)" />
71+
72+
<div
73+
v-if="needShowMore"
74+
class="flex justify-end bottom-0 right-0 bg-white pl-0.5 text-dark-blue"
75+
:class="{ absolute: !showMore, 'w-full': showMore }"
76+
>
77+
<button @click="onToggleShowMore">
78+
{{ showMore ? 'Show less' : '... Show more' }}
79+
</button>
80+
</div>
81+
</div>
6582
</div>
6683

6784
<div class="mt-auto flex-shrink-0 h-[56px]">
@@ -92,5 +109,68 @@ export default {
92109
props: {
93110
tool: Object,
94111
},
112+
data() {
113+
return {
114+
descriptionHeight: 'auto',
115+
needShowMore: true,
116+
showMore: false,
117+
};
118+
},
119+
methods: {
120+
escapeHtml(value = '') {
121+
return String(value)
122+
.replace(/&/g, '&amp;')
123+
.replace(/</g, '&lt;')
124+
.replace(/>/g, '&gt;')
125+
.replace(/"/g, '&quot;')
126+
.replace(/'/g, '&#39;');
127+
},
128+
formatMultiline(value) {
129+
if (!value) return '';
130+
const normalized = String(value).replace(/\r\n?/g, '\n');
131+
return this.escapeHtml(normalized).replace(/\n/g, '<br>');
132+
},
133+
computeDescriptionHeight() {
134+
const containerEl = this.$refs.descriptionContainerRef;
135+
const descriptionEl = this.$refs.descriptionRef;
136+
if (!containerEl || !descriptionEl) {
137+
return;
138+
}
139+
140+
const maxHeight = containerEl.clientHeight;
141+
const rows = Math.floor(maxHeight / 22);
142+
descriptionEl.style.height = 'auto';
143+
this.descriptionHeight = 'auto';
144+
145+
this.needShowMore = descriptionEl.offsetHeight > maxHeight;
146+
if (descriptionEl.offsetHeight > maxHeight) {
147+
descriptionEl.style.height = `${rows * 22}px`;
148+
this.descriptionHeight = `${rows * 22}px`;
149+
} else {
150+
this.showMore = false;
151+
}
152+
},
153+
onToggleShowMore() {
154+
const descriptionEl = this.$refs.descriptionRef;
155+
if (!descriptionEl) {
156+
return;
157+
}
158+
159+
this.showMore = !this.showMore;
160+
if (!this.showMore) {
161+
descriptionEl.style.height = this.descriptionHeight;
162+
} else {
163+
descriptionEl.style.height = 'auto';
164+
}
165+
},
166+
},
167+
mounted: function () {
168+
if (this.tool.description) {
169+
this.computeDescriptionHeight();
170+
} else {
171+
this.needShowMore = false;
172+
this.showMore = false;
173+
}
174+
},
95175
};
96176
</script>

0 commit comments

Comments
 (0)