@@ -3,7 +3,7 @@ import { faStar as farStar } from "@fortawesome/free-regular-svg-icons";
33import { faCaretDown , faEdit , faPen , faSpinner , faStar , type IconDefinition } from " @fortawesome/free-solid-svg-icons" ;
44import { FontAwesomeIcon } from " @fortawesome/vue-fontawesome" ;
55import { BBadge , BButton , BButtonGroup , BDropdown , BDropdownItem , BFormCheckbox , BLink } from " bootstrap-vue" ;
6- import { ref } from " vue" ;
6+ import { computed , ref } from " vue" ;
77
88import { useMarkdown } from " @/composables/markdown" ;
99import { useUid } from " @/composables/utils/uid" ;
@@ -152,6 +152,11 @@ interface Props {
152152 */
153153 titleIcon? : TitleIcon ;
154154
155+ /** Whether the title should be truncated to a certain number of lines
156+ * @default undefined
157+ */
158+ titleNLines? : number ;
159+
155160 /** Size of the card title
156161 * @default " sm"
157162 */
@@ -197,6 +202,7 @@ const props = withDefaults(defineProps<Props>(), {
197202 title: " " ,
198203 titleBadges : () => [],
199204 titleIcon: undefined ,
205+ titleNLines: undefined ,
200206 titleSize: " sm" ,
201207 updateTime: " " ,
202208 updateTimeIcon : () => faEdit ,
@@ -268,14 +274,19 @@ const getElementId = (cardId: string, element: string) => `g-card-${element}-${c
268274const getIndicatorId = (cardId : string , indicatorId : string ) => ` g-card-indicator-${indicatorId }-${cardId } ` ;
269275const getBadgeId = (cardId : string , badgeId : string ) => ` g-card-badge-${badgeId }-${cardId } ` ;
270276const getActionId = (cardId : string , actionId : string ) => ` g-card-action-${actionId }-${cardId } ` ;
277+
278+ /**
279+ * Number of lines before title truncation (undefined = no truncation)
280+ */
281+ const allowedTitleLines = computed (() => props .titleNLines );
271282 </script >
272283
273284<template >
274285 <component
275286 :is =" 'div'"
276287 :id =" `g-card-${props.id}`"
277288 :role =" props.clickable ? 'button' : undefined"
278- class =" g-card pt-0 px-1 pb -2"
289+ class =" g-card pt-0 px-1 mb -2"
279290 :class =" [
280291 { 'g-card-grid-view': gridView },
281292 { 'g-card-selected': selected },
@@ -301,7 +312,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
301312 <slot name =" select" >
302313 <BFormCheckbox
303314 :id =" getElementId(props.id, 'select')"
304- v-b-tooltip.hover
315+ v-b-tooltip.hover.noninteractive
305316 :checked =" selected"
306317 :title =" props.selectTitle || localize('Select for bulk actions')"
307318 @change =" emit('select')" />
@@ -314,7 +325,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
314325 :id =" getElementId(props.id, 'title')"
315326 bold
316327 inline
317- class =" d-inline "
328+ class =" align-items-baseline "
318329 :size =" props.titleSize" >
319330 <FontAwesomeIcon
320331 v-if =" props.titleIcon?.icon"
@@ -323,17 +334,23 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
323334 :title =" props.titleIcon.title"
324335 :size =" props.titleIcon.size"
325336 fixed-width />
326-
327337 <BLink
328338 v-if =" typeof title === 'object'"
329339 :id =" getElementId(props.id, 'title-link')"
330- v-b-tooltip.hover
340+ v-b-tooltip.hover.noninteractive
331341 :title =" localize(title.title)"
342+ :class =" { 'g-card-title-truncate': props.titleNLines }"
332343 @click.stop.prevent =" title.handler" >
333344 {{ title.label }}
334345 </BLink >
335346 <template v-else >
336- <span :id =" getElementId(props.id, 'title-text')" >{{ title }}</span >
347+ <span
348+ :id =" getElementId(props.id, 'title-text')"
349+ v-b-tooltip.hover.noninteractive
350+ :title =" localize(title)"
351+ :class =" { 'g-card-title-truncate': props.titleNLines }" >
352+ {{ title }}
353+ </span >
337354 </template >
338355
339356 <slot name =" titleActions" >
@@ -360,7 +377,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
360377 v-if =" badge.visible ?? true"
361378 :id =" getBadgeId(props.id, badge.id)"
362379 :key =" badge.id"
363- v-b-tooltip.hover
380+ v-b-tooltip.hover.noninteractive
364381 :pill =" badge.type !== 'badge'"
365382 class =" mt-1"
366383 :class =" {
@@ -391,7 +408,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
391408 props.bookmarked ? 'bookmark-remove' : 'bookmark-add'
392409 )
393410 "
394- v-b-tooltip.hover
411+ v-b-tooltip.hover.noninteractive
395412 class =" inline-icon-button"
396413 variant =" link"
397414 :title =" props.bookmarked ? 'Remove bookmark' : 'Add to bookmarks'"
@@ -401,7 +418,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
401418 <BButton
402419 v-else
403420 :id =" getElementId(props.id, 'bookmark-loading')"
404- v-b-tooltip.hover
421+ v-b-tooltip.hover.noninteractive
405422 class =" inline-icon-button"
406423 variant =" link"
407424 :title =" localize('Bookmarking...')"
@@ -486,7 +503,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
486503 v-if =" (indicator.visible ?? true) && !indicator.disabled"
487504 :id =" getIndicatorId(props.id, indicator.id)"
488505 :key =" indicator.id"
489- v-b-tooltip.hover
506+ v-b-tooltip.hover.noninteractive
490507 class =" inline-icon-button"
491508 :title =" localize(indicator.title)"
492509 :variant =" indicator.variant || 'outline-secondary'"
@@ -506,7 +523,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
506523 v-else-if =" (indicator.visible ?? true) && indicator.disabled"
507524 :id =" getIndicatorId(props.id, indicator.id)"
508525 :key =" indicator.id"
509- v-b-tooltip.hover
526+ v-b-tooltip.hover.noninteractive
510527 :title =" localize(indicator.title)"
511528 :icon =" indicator.icon"
512529 :size =" indicator.size || 'sm'"
@@ -575,7 +592,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
575592 v-if =" sa.visible ?? true"
576593 :id =" getActionId(props.id, sa.id)"
577594 :key =" sa.id"
578- v-b-tooltip.hover
595+ v-b-tooltip.hover.noninteractive
579596 :disabled =" sa.disabled"
580597 :title =" localize(sa.title)"
581598 :variant =" sa.variant || 'outline-primary'"
@@ -604,7 +621,7 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
604621 v-if =" pa.visible ?? true"
605622 :id =" getActionId(props.id, pa.id)"
606623 :key =" pa.id"
607- v-b-tooltip.hover
624+ v-b-tooltip.hover.noninteractive
608625 :disabled =" pa.disabled"
609626 :title =" localize(pa.title)"
610627 :variant =" pa.variant || 'primary'"
@@ -686,6 +703,17 @@ const getActionId = (cardId: string, actionId: string) => `g-card-action-${actio
686703 border : 1px solid $brand-secondary ;
687704 border-radius : 0.5rem ;
688705
706+ .g-card-title-truncate {
707+ display : -webkit-box ;
708+ -webkit-box-orient : vertical ;
709+ -webkit-line-clamp : v-bind (allowedTitleLines );
710+ line-clamp : v-bind (allowedTitleLines );
711+ overflow : hidden ;
712+ line-height : 1.2 ;
713+ white-space : normal ;
714+ text-overflow : unset ;
715+ }
716+
689717 .g-card-secondary-action-label {
690718 @container g-card (max-width : #{$breakpoint-sm } ) {
691719 display : none ;
0 commit comments