97399 Added support for links in tooltips

This commit is contained in:
Koen Pauwels
2022-12-19 14:55:55 +01:00
parent 28a127ca0e
commit 1ae18ec9f7
6 changed files with 96 additions and 13 deletions

View File

@@ -64,7 +64,11 @@
</p> </p>
<ul class="footer-info list-unstyled small d-flex justify-content-center mb-0"> <ul class="footer-info list-unstyled small d-flex justify-content-center mb-0">
<li> <li>
<a class="text-white" href="javascript:void(0);" <a *dsContextHelp="{content: 'context-help.multi-para.test',
tooltipPlacement: 'top',
iconPlacement: 'left'
}"
class="text-white" href="javascript:void(0);"
(click)="showCookieSettings()">{{ 'footer.link.cookies' | translate}}</a> (click)="showCookieSettings()">{{ 'footer.link.cookies' | translate}}</a>
</li> </li>
<li> <li>

View File

@@ -1,9 +1,21 @@
<ng-template #help> <ng-template #help>
<div class="preserve-line-breaks ds-context-help-content">{{ content | translate }}</div> <div class="preserve-line-breaks ds-context-help-content">
<ng-container *ngFor="let elem of (parsedContent$ | async)">
<ng-container *ngIf="elem.href">
<a href="{{elem.href}}" target="_blank">{{elem.text}}</a>
</ng-container>
<ng-container *ngIf="elem.href === undefined">
{{ elem }}
</ng-container>
</ng-container>
<!-- {{ content | translate }} -->
</div>
</ng-template> </ng-template>
<i [ngClass]="{'ds-context-help-icon fas fa-question-circle shadow-sm': true, <i [ngClass]="{'ds-context-help-icon fas fa-question-circle shadow-sm': true,
'ds-context-help-icon-right': iconPlacement == 'right', 'ds-context-help-icon-right': iconPlacement !== 'left',
'ds-context-help-icon-left': iconPlacement == 'left'}" 'ds-context-help-icon-left': iconPlacement === 'left'}"
[ngbTooltip]="help" [ngbTooltip]="help"
[placement]="tooltipPlacement" [placement]="tooltipPlacement"
container="'body'" container="'body'"

View File

@@ -23,5 +23,5 @@
} }
::ng-deep .tooltip-inner { ::ng-deep .tooltip-inner {
max-width: 300px; width: 300px;
} }

View File

@@ -1,10 +1,10 @@
import { Component, Input, OnInit, TemplateRef } from '@angular/core'; import { Component, Input, OnInit, TemplateRef } from '@angular/core';
import { PlacementArray } from '@ng-bootstrap/ng-bootstrap/util/positioning'; import { PlacementArray } from '@ng-bootstrap/ng-bootstrap/util/positioning';
import { TranslateService } from '@ngx-translate/core';
import { Observable, of as observableOf } from 'rxjs';
import { map } from 'rxjs/operators';
import { PlacementDir } from './placement-dir.model'; import { PlacementDir } from './placement-dir.model';
// export type PlacementDir = 'left' | 'right';
@Component({ @Component({
selector: 'ds-context-help-wrapper', selector: 'ds-context-help-wrapper',
templateUrl: './context-help-wrapper.component.html', templateUrl: './context-help-wrapper.component.html',
@@ -12,9 +12,65 @@ import { PlacementDir } from './placement-dir.model';
}) })
export class ContextHelpWrapperComponent { export class ContextHelpWrapperComponent {
@Input() templateRef: TemplateRef<any>; @Input() templateRef: TemplateRef<any>;
@Input() content: string; @Input() tooltipPlacement?: PlacementArray;
@Input() tooltipPlacement: PlacementArray; @Input() iconPlacement?: PlacementDir;
@Input() iconPlacement: PlacementDir; @Input() dontParseLinks?: boolean;
constructor() { } // TODO: dependent on evaluation order of input setters?
parsedContent$: Observable<(string | {href: string, text: string})[]> = observableOf([]);
@Input() set content(content : string) {
this.parsedContent$ = this.translateService.get(content).pipe(
map(this.dontParseLinks
? ((text: string) => [text])
: this.parseLinks)
);
}
constructor(private translateService: TranslateService) { }
/*
* Parses Markdown-style links, splitting up a given text
* into link-free pieces of text and objects of the form
* {href: string, text: string} (which represent links).
* This function makes no effort to check whether the href is a
* correct URL. Currently this function does not support escape
* characters: its behavior when given a string containing square
* brackets that do not deliminate a link is undefined.
* Regular parentheses outside of links do work, however.
*
* For example:
* parseLinks("This is text, [this](https://google.com) is a link, and [so is this](https://youtube.com)")
* =
* [ "This is text, ",
* {href: "https://google.com", text: "this"},
* " is a link, and ",
* {href: "https://youtube.com", text: "so is this"}
* ]
*/
private parseLinks(content: string): (string | {href: string, text: string})[] {
// Implementation note: due to unavailability of `matchAll` method on strings,
// separate "split" and "parse" steps are needed.
// We use splitRegexp (the outer `match` call) to split the text
// into link-free pieces of text (matched by /[^\[]+/) and pieces
// of text of the form "[some link text](some.link.here)" (matched
// by /\[([^\]]*)\]\(([^\)]*)\)/)
const splitRegexp = /[^\[]+|\[([^\]]*)\]\(([^\)]*)\)/g;
// Once the array is split up in link-representing strings and
// non-link-representing strings, we use parseRegexp (the inner
// `match` call) to transform the link-representing strings into
// {href: string, text: string} objects.
const parseRegexp = /^\[([^\]]*)\]\(([^\)]*)\)$/;
return content.match(splitRegexp).map((substring: string) => {
const match = substring.match(parseRegexp);
return match === null
? substring
: ({href: match[2], text: match[1]});
});
}
} }

View File

@@ -9,10 +9,21 @@ export type ContextHelpDirectiveInput = {
iconPlacement?: PlacementDir iconPlacement?: PlacementDir
} }
/*
* Directive to add a clickable tooltip icon to an element.
* The tooltip icon's position is configurable ('left' or 'right')
* and so is the position of the tooltip itself (PlacementArray).
*/
@Directive({ @Directive({
selector: '[dsContextHelp]', selector: '[dsContextHelp]',
}) })
export class ContextHelpDirective implements OnChanges { export class ContextHelpDirective implements OnChanges {
/*
* Expects an object with the following fields:
* - content: a string referring to an entry in the i18n files
* - tooltipPlacement: a PlacementArray describing where the tooltip should expand, relative to the tooltip icon
* - iconPlacement: a string 'left' or 'right', describing where the tooltip icon should be placed, relative to the element
*/
@Input() dsContextHelp: string | ContextHelpDirectiveInput; @Input() dsContextHelp: string | ContextHelpDirectiveInput;
protected wrapper: ComponentRef<ContextHelpWrapperComponent>; protected wrapper: ComponentRef<ContextHelpWrapperComponent>;

View File

@@ -4164,6 +4164,6 @@
"idle-modal.extend-session": "Extend session", "idle-modal.extend-session": "Extend session",
"context-help.multi-para.test": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In facilisis ac metus ut tristique. Integer eros risus, efficitur non urna eu, ultrices mollis felis. Nullam mi sapien, mattis eu nunc in, rhoncus vulputate risus. \n\nDuis pharetra et nibh aliquam sodales. Integer ut rutrum dolor, ut maximus quam. Nunc sit amet maximus urna. In nec erat vulputate, facilisis nulla a, scelerisque orci. Ut pellentesque sapien et eros euismod scelerisque finibus at sem. Duis egestas convallis ligula nec laoreet. \n\nProin ut magna sit amet libero consequat ultrices et sed mi. Nam congue sed risus ac fringilla. Suspendisse potenti. Cras mattis, est non laoreet elementum, erat enim interdum tortor, eu placerat enim metus sit amet enim.", "context-help.multi-para.test": "Lorem ipsum dolor [sit amet](https://google.com), consectetur adipiscing [elit](https://youtube.com). In facilisis ac metus ut tristique. Integer eros risus, efficitur non urna eu, ultrices mollis felis. Nullam mi sapien, mattis eu nunc in, rhoncus vulputate risus. \n\nDuis pharetra et nibh aliquam sodales. Integer ut rutrum dolor, ut maximus quam. Nunc sit amet maximus urna. In nec erat vulputate, facilisis nulla a, scelerisque orci. Ut pellentesque sapien et eros euismod scelerisque finibus at sem. Duis egestas convallis ligula nec laoreet. \n\nProin ut magna sit amet libero consequat ultrices et sed mi. Nam congue sed risus ac fringilla. Suspendisse potenti. Cras mattis, est non laoreet elementum, erat enim interdum tortor, eu placerat enim metus sit amet enim.",
} }