Browse Source

feat(iot-hub): force markdown links to new tab + verified item-link card

- TbIotHubMarkdownComponent.parseData now runs through a
  forceLinksOpenInNewTab post-pass that (a) appends the
  {:target="_blank"} suffix to the TEXT of any markdown link
  [text](url) that doesn't already carry it, matching the
  MarkedOptionsService.renderer.link contract that strips the
  suffix and rewrites the <a> with target="_blank", and (b)
  injects target="_blank" into raw <a ...> HTML anchors that
  don't already declare a target= attribute. Image syntax
  ![alt](url) is excluded via lookbehind.
- iot-hub-markdown SCSS narrows the link styling selector to
  a:not(.mdc-button):not(.tb-iot-hub-item-link-card) so the
  embedded item-link card anchor keeps its own typography.
- iot-hub-item-link-card now renders a verified icon (teal
  #00695c via a new tb-iot-hub-item-link-verified-icon class)
  when item.creatorVerified is true, matching the item-card
  treatment; the default person icon is used otherwise.
pull/15772/head
Igor Kulikov 4 months ago
parent
commit
18232e9c8c
  1. 6
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.html
  2. 8
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.scss
  3. 2
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.scss
  4. 27
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.ts

6
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.html

@ -47,7 +47,11 @@
<div class="tb-iot-hub-item-link-text">
<span class="tb-iot-hub-item-link-name">{{ item.name }}</span>
<span class="tb-iot-hub-item-link-author">
<mat-icon>person</mat-icon>
@if (item.creatorVerified) {
<mat-icon class="tb-iot-hub-item-link-verified-icon">verified</mat-icon>
} @else {
<mat-icon>person</mat-icon>
}
{{ item.creatorDisplayName }}
</span>
</div>

8
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.scss

@ -37,6 +37,9 @@
background: rgba(4, 138, 211, 0.04);
border-color: rgba(4, 138, 211, 0.32);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
.tb-iot-hub-item-link-name, .tb-iot-hub-item-link-author {
text-decoration: underline;
}
}
&--unavailable {
@ -128,6 +131,11 @@
width: 14px;
height: 14px;
}
// Verified-creator accent — matches the item card chip color.
.tb-iot-hub-item-link-verified-icon {
color: #00695c;
}
}
.tb-iot-hub-item-link-skeleton-block,

2
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.scss

@ -128,7 +128,7 @@ $gallery-border-hover-color: #2a7dec;
max-width: 100%;
}
a:not(.mdc-button) {
a:not(.mdc-button):not(.tb-iot-hub-item-link-card) {
font-weight: 500;
color: $link-color;
text-decoration: none;

27
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-markdown.component.ts

@ -109,9 +109,36 @@ export class TbIotHubMarkdownComponent implements OnInit, OnChanges {
parsed = replaceItemLinkPlaceholders(parsed);
parsed = this.resolveImages(parsed);
parsed = this.resolveVariables(parsed);
parsed = this.forceLinksOpenInNewTab(parsed);
return parsed;
}
// Ensure every link rendered through tb-markdown opens in a new tab.
// 1) For markdown links `[text](url)` — append the
// `{:target="_blank"}` suffix to the link TEXT (not the URL)
// so MarkedOptionsService.renderer.link, which checks
// `token.text.endsWith(targetBlankBlock)`, emits
// `target="_blank"` on the rendered <a>. Skip links whose
// text already ends with the suffix and skip image syntax
// (`![alt](url)`).
// 2) For raw <a ...> HTML anchors authored in the markdown — add
// `target="_blank"` when no `target=` attribute is already
// present.
private forceLinksOpenInNewTab(content: string): string {
const TARGET_BLANK_BLOCK = '{:target="_blank"}';
content = content.replace(
/(?<!!)\[([^\]]*?)]\(([^)]+)\)/g,
(match, text: string, url: string) =>
text.endsWith(TARGET_BLANK_BLOCK) ? match : `[${text}${TARGET_BLANK_BLOCK}](${url})`
);
content = content.replace(
/<a\b([^>]*)>/gi,
(match, attrs: string) =>
/\btarget\s*=/i.test(attrs) ? match : `<a${attrs} target="_blank">`
);
return content;
}
private prefixResourceUrls(markdown: string): string {
const baseUrl = this.iotHubApiService.baseUrl;
return markdown.replace(/([("])(\/api\/resources\/[^)"]*)/g, `$1${baseUrl}$2`);

Loading…
Cancel
Save