@@ -176,6 +176,11 @@ interface Props {
176176 * @default " Last updated"
177177 */
178178 updateTimeTitle? : string ;
179+
180+ /** Whether this card is highlighted (for example, as a range selection anchor)
181+ * @default false
182+ */
183+ highlighted? : boolean ;
179184}
180185
181186const props = withDefaults (defineProps <Props >(), {
@@ -207,6 +212,7 @@ const props = withDefaults(defineProps<Props>(), {
207212 updateTime: " " ,
208213 updateTimeIcon : () => faEdit ,
209214 updateTimeTitle: " Last updated" ,
215+ highlighted: false ,
210216});
211217
212218/**
@@ -266,7 +272,7 @@ async function toggleBookmark() {
266272 bookmarkLoading .value = false ;
267273}
268274
269- const { renderMarkdown } = useMarkdown ({ openLinksInNewPage: true });
275+ const { renderMarkdown } = useMarkdown ({ noMargin: true , openLinksInNewPage: true });
270276
271277/**
272278 * Helper functions for generating consistent element IDs
@@ -283,8 +289,10 @@ const allowedTitleLines = computed(() => props.titleNLines);
283289
284290function onKeyDown(event : KeyboardEvent ) {
285291 if ((props .clickable && event .key === " Enter" ) || event .key === " " ) {
292+ event .stopPropagation ();
286293 emit (" click" , event );
287294 } else if (props .clickable ) {
295+ event .stopPropagation ();
288296 emit (" keydown" , event );
289297 }
290298}
@@ -310,7 +318,7 @@ function onKeyDown(event: KeyboardEvent) {
310318 <div
311319 :id =" `g-card-content-${props.id}`"
312320 class =" g-card-content d-flex flex-column justify-content-between h-100 p-2"
313- :class =" contentClass" >
321+ :class =" [{ 'g-card-highlighted': props.highlighted }, contentClass] " >
314322 <slot >
315323 <div class =" d-flex flex-column flex-gapy-1" >
316324 <div
@@ -335,12 +343,13 @@ function onKeyDown(event: KeyboardEvent) {
335343 :id =" getElementId(props.id, 'title')"
336344 bold
337345 inline
338- class =" align-items-baseline "
346+ class =" d-block "
339347 :size =" props.titleSize" >
340348 <FontAwesomeIcon
341349 v-if =" props.titleIcon?.icon"
342- :icon = " props.titleIcon.icon "
350+ class = " mr-1 "
343351 :class =" props.titleIcon.class"
352+ :icon =" props.titleIcon.icon"
344353 :title =" props.titleIcon.title"
345354 :size =" props.titleIcon.size"
346355 fixed-width />
@@ -516,7 +525,7 @@ function onKeyDown(event: KeyboardEvent) {
516525 <BButton
517526 v-if =" (indicator.visible ?? true) && !indicator.disabled"
518527 :id =" getIndicatorId(props.id, indicator.id)"
519- :key =" indicator.id"
528+ :key =" `${ indicator.id}-button` "
520529 v-b-tooltip.hover.noninteractive
521530 class =" inline-icon-button"
522531 :title =" localize(indicator.title)"
@@ -536,7 +545,7 @@ function onKeyDown(event: KeyboardEvent) {
536545 <FontAwesomeIcon
537546 v-else-if =" (indicator.visible ?? true) && indicator.disabled"
538547 :id =" getIndicatorId(props.id, indicator.id)"
539- :key =" indicator.id"
548+ :key =" `${ indicator.id}-icon` "
540549 v-b-tooltip.hover.noninteractive
541550 :title =" localize(indicator.title)"
542551 :icon =" indicator.icon"
@@ -549,16 +558,15 @@ function onKeyDown(event: KeyboardEvent) {
549558 </div >
550559 </div >
551560
552- <div :id =" getElementId(props.id, 'description')" >
561+ <div :id =" getElementId(props.id, 'description')" class = " g-card-description " >
553562 <slot name =" description" >
554- <TextSummary
555- v-if =" props.description && !props.fullDescription"
556- :id =" getElementId(props.id, 'text-summary')"
557- :description =" props.description" />
558- <div
559- v-else-if =" props.description && props.fullDescription"
560- class =" mb-2"
561- v-html =" renderMarkdown(props.description)" />
563+ <template v-if =" props .description " >
564+ <TextSummary
565+ v-if =" !props.fullDescription"
566+ :id =" getElementId(props.id, 'text-summary')"
567+ :description =" props.description" />
568+ <div v-else v-html =" renderMarkdown(props.description)" />
569+ </template >
562570 </slot >
563571 </div >
564572 </div >
@@ -598,9 +606,13 @@ function onKeyDown(event: KeyboardEvent) {
598606 </div >
599607 </slot >
600608
601- <div class =" align-items-center d-flex flex-gapx-1 justify-content-end ml-auto mt-1 " >
609+ <div class =" align-items-center d-flex flex-gapx-1 justify-content-end ml-auto" >
602610 <slot name =" secondary-actions" >
603- <BButtonGroup :id =" getElementId(props.id, 'secondary-actions')" size =" sm" >
611+ <BButtonGroup
612+ v-if =" props.secondaryActions?.length"
613+ :id =" getElementId(props.id, 'secondary-actions')"
614+ size =" sm"
615+ class =" mt-1" >
604616 <template v-for =" sa in props .secondaryActions " >
605617 <BButton
606618 v-if =" sa.visible ?? true"
@@ -630,30 +642,33 @@ function onKeyDown(event: KeyboardEvent) {
630642
631643 <div :id =" getElementId(props.id, 'primary-actions')" class =" d-flex flex-gapx-1" >
632644 <slot name =" primary-actions" >
633- <template v-for =" pa in props .primaryActions " >
634- <BButton
635- v-if =" pa.visible ?? true"
636- :id =" getActionId(props.id, pa.id)"
637- :key =" pa.id"
638- v-b-tooltip.hover.noninteractive
639- :disabled =" pa.disabled"
640- :title =" localize(pa.title)"
641- :variant =" pa.variant || 'primary'"
642- :size =" pa.size || 'sm'"
643- :to =" pa.to"
644- :href =" pa.href"
645- :class =" {
646- 'inline-icon-button': pa.inline,
647- [String(pa.class)]: pa.class,
648- }"
649- @click.stop =" pa.handler" >
650- <FontAwesomeIcon
651- v-if =" pa.icon"
652- :icon =" pa.icon"
653- :size =" pa.size || undefined"
654- fixed-width />
655- {{ localize(pa.label) }}
656- </BButton >
645+ <template v-if =" props .primaryActions ?.length " >
646+ <template v-for =" pa in props .primaryActions " >
647+ <BButton
648+ v-if =" pa.visible ?? true"
649+ :id =" getActionId(props.id, pa.id)"
650+ :key =" pa.id"
651+ v-b-tooltip.hover.noninteractive
652+ class =" mt-1"
653+ :disabled =" pa.disabled"
654+ :title =" localize(pa.title)"
655+ :variant =" pa.variant || 'primary'"
656+ :size =" pa.size || 'sm'"
657+ :to =" pa.to"
658+ :href =" pa.href"
659+ :class =" {
660+ 'inline-icon-button': pa.inline,
661+ [String(pa.class)]: pa.class,
662+ }"
663+ @click.stop =" pa.handler" >
664+ <FontAwesomeIcon
665+ v-if =" pa.icon"
666+ :icon =" pa.icon"
667+ :size =" pa.size || undefined"
668+ fixed-width />
669+ {{ localize(pa.label) }}
670+ </BButton >
671+ </template >
657672 </template >
658673 </slot >
659674 </div >
@@ -701,6 +716,10 @@ function onKeyDown(event: KeyboardEvent) {
701716 border-left : 0.25rem solid $brand-primary ;
702717 }
703718
719+ & .g-card-highlighted .g-card-content {
720+ box-shadow : 0 0 0 0.2rem transparentize ($brand-primary , 0.75 );
721+ }
722+
704723 & .g-card-clickable {
705724 cursor : pointer ;
706725
0 commit comments