diff --git a/demo/src/app/cheat-sheet/cheat-sheet.component.html b/demo/src/app/cheat-sheet/cheat-sheet.component.html
index 8becb04c..67e57da1 100644
--- a/demo/src/app/cheat-sheet/cheat-sheet.component.html
+++ b/demo/src/app/cheat-sheet/cheat-sheet.component.html
@@ -29,7 +29,20 @@
Lists
Links
{{ links$ | async }}
-
+
@@ -61,4 +74,4 @@ Horizontal Rule
{{ horizontalRule$ | async }}
-
\ No newline at end of file
+
diff --git a/demo/src/app/cheat-sheet/remote/links.md b/demo/src/app/cheat-sheet/remote/links.md
index 4d6cd1f5..ede829cb 100644
--- a/demo/src/app/cheat-sheet/remote/links.md
+++ b/demo/src/app/cheat-sheet/remote/links.md
@@ -10,6 +10,21 @@ There are two ways to create links.
[You can use numbers for reference-style link definitions][1]
+[I'm a router link](routerLink:/get-started)
+
+```html
+
+```
+
Or leave it empty and use the [link text itself].
URLs and URLs in angle brackets will automatically get turned into links.
@@ -20,4 +35,4 @@ Some text to show that the reference links can follow later.
[arbitrary case-insensitive reference text]: https://www.mozilla.org
[1]: http://slashdot.org
-[link text itself]: http://www.reddit.com
\ No newline at end of file
+[link text itself]: http://www.reddit.com
diff --git a/lib/src/markdown.component.ts b/lib/src/markdown.component.ts
index 3a275dcc..d5f284ee 100644
--- a/lib/src/markdown.component.ts
+++ b/lib/src/markdown.component.ts
@@ -1,27 +1,40 @@
+import { CommonModule } from '@angular/common';
import {
AfterViewInit,
Component,
ElementRef,
EventEmitter,
+ HostListener,
Input,
OnChanges,
OnDestroy,
+ Optional,
Output,
TemplateRef,
Type,
ViewContainerRef,
} from '@angular/core';
-import { Subject } from 'rxjs';
-import { takeUntil } from 'rxjs/operators';
+import { NavigationExtras, Router } from '@angular/router';
+import { from, merge, Subject } from 'rxjs';
+import { filter, map, switchMap, takeUntil, tap } from 'rxjs/operators';
import { KatexOptions } from './katex-options';
import { MarkdownService, ParseOptions, RenderOptions } from './markdown.service';
import { MermaidAPI } from './mermaid-options';
import { PrismPlugin } from './prism-plugin';
+export interface MarkdownRouterLinkOptions {
+ global?: NavigationExtras;
+ paths?: { [path: string]: NavigationExtras | undefined };
+}
+
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'markdown, [markdown]',
- template: '',
+ template: `
+
+
+ `,
+ imports: [CommonModule],
standalone: true,
})
export class MarkdownComponent implements OnChanges, AfterViewInit, OnDestroy {
@@ -97,12 +110,57 @@ export class MarkdownComponent implements OnChanges, AfterViewInit, OnDestroy {
@Input() prompt: string | undefined;
@Input() output: string | undefined;
@Input() user: string | undefined;
+ @Input() routerLinkOptions: MarkdownRouterLinkOptions | undefined;
// Event emitters
@Output() error = new EventEmitter();
@Output() load = new EventEmitter();
@Output() ready = new EventEmitter();
+ private changed = new Subject();
+ /**
+ * When the markdown content is ready, or when the markdown content changes, this observable emits.
+ * - Get all the anchor tags in the markdown content.
+ * - Filter the anchor tags that have a `href` attribute that starts with `/routerLink:`.
+ * - Set the `data-routerLink` attribute to the `href` attribute without the `/routerLink:` prefix.
+ * - Remove the `/routerLink:` prefix from the `href` attribute.
+ */
+ protected changed$ = merge(this.ready, this.changed).pipe(
+ map(() => this.element.nativeElement.querySelectorAll('a')),
+ switchMap(links => from(links)),
+ filter(link => link.getAttribute('href')?.startsWith('/routerLink:') === true),
+ tap(link => link.setAttribute('data-routerLink', link.getAttribute('href')!.replace('/routerLink:', ''))),
+ tap(link => link.setAttribute('href', link.getAttribute('href')!.replace('/routerLink:', ''))),
+ );
+
+ @HostListener('click', ['$event'])
+ onDocumentClick(event: MouseEvent) {
+ const target = event.target as HTMLElement;
+ const anchor = target.nodeName.toLowerCase() === 'a' ? target : target.closest('a');
+ const path = anchor?.getAttribute('href');
+ if (path && anchor) {
+ // Stop the browser from navigating
+ event.preventDefault();
+ event.stopPropagation();
+
+ // Get the routerLink commands to navigate to
+ const commands = anchor.getAttribute('data-routerLink')!.split('/').filter(String);
+
+ let extras: NavigationExtras | undefined;
+ // Find the path in the routerLinkOptions
+ if (this.routerLinkOptions?.paths) {
+ extras = this.routerLinkOptions.paths[path];
+ }
+ // Get the global options if no path was found
+ if (!extras && this.routerLinkOptions?.global) {
+ extras = this.routerLinkOptions.global;
+ }
+
+ // Navigate to the path using the router service
+ this.router?.navigate(commands, extras);
+ }
+ }
+
private _clipboard = false;
private _commandLine = false;
private _disableSanitizer = false;
@@ -119,10 +177,12 @@ export class MarkdownComponent implements OnChanges, AfterViewInit, OnDestroy {
public element: ElementRef,
public markdownService: MarkdownService,
public viewContainerRef: ViewContainerRef,
+ @Optional() public router?: Router,
) { }
ngOnChanges(): void {
this.loadContent();
+ this.changed.next();
}
loadContent(): void {