132 lines
3.8 KiB
Markdown
132 lines
3.8 KiB
Markdown
# MarkdownViewerComponent
|
|
|
|
Selector: `app-markdown-viewer`
|
|
Module: `AppSharedModule` (already exported — no extra import needed)
|
|
|
|
A generic, self-contained markdown renderer. Handles parsing, heading-based section splitting, Mermaid diagrams, inline video embeds, syntax-highlighted find-in-page, and emits a table-of-contents item list for the host to render wherever it likes.
|
|
|
|
---
|
|
|
|
## Inputs
|
|
|
|
| Input | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `src` | `string` | `undefined` | URL of a remote `.md` file to fetch and render. Mutually exclusive with `markdown`. |
|
|
| `markdown` | `string` | `undefined` | Raw markdown string to render inline. Takes precedence over `src` if both are set. |
|
|
| `showFindBar` | `boolean` | `false` | Show the find-in-page bar above the content. |
|
|
|
|
---
|
|
|
|
## Outputs
|
|
|
|
| Output | Payload | Description |
|
|
|---|---|---|
|
|
| `tocItemsChange` | `{ label: string; anchorId: string }[]` | Emitted after content loads. Each item corresponds to a top-level heading in the document. Use this to render a Table of Contents outside the component. |
|
|
|
|
---
|
|
|
|
## Public Methods
|
|
|
|
| Method | Signature | Description |
|
|
|---|---|---|
|
|
| `scrollToId` | `(anchorId: string) => void` | Scrolls the content area to the element with the given id. Use in conjunction with `tocItemsChange` to implement external TOC navigation. |
|
|
|
|
---
|
|
|
|
## Usage Examples
|
|
|
|
### Render a remote file
|
|
|
|
```html
|
|
<app-markdown-viewer src="/assets/docs/readme.md"></app-markdown-viewer>
|
|
```
|
|
|
|
### Render an inline string
|
|
|
|
```html
|
|
<app-markdown-viewer [markdown]="myMarkdownString"></app-markdown-viewer>
|
|
```
|
|
|
|
### With find-in-page bar
|
|
|
|
```html
|
|
<app-markdown-viewer [markdown]="content" [showFindBar]="true"></app-markdown-viewer>
|
|
```
|
|
|
|
### With an external Table of Contents
|
|
|
|
The component emits TOC items but does **not** render a TOC sidebar itself. The host component is responsible for displaying the list and wiring up scroll navigation.
|
|
|
|
**Template:**
|
|
```html
|
|
<!-- TOC rendered by the host -->
|
|
<ul>
|
|
<li *ngFor="let item of tocItems">
|
|
<a href="#" (click)="scrollToHeading($event, item.anchorId)">{{ item.label }}</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Viewer -->
|
|
<app-markdown-viewer
|
|
#viewer
|
|
[markdown]="content"
|
|
(tocItemsChange)="tocItems = $event">
|
|
</app-markdown-viewer>
|
|
```
|
|
|
|
**Component:**
|
|
```typescript
|
|
import { ViewChild } from '@angular/core';
|
|
import { MarkdownViewerComponent } from '../shared/markdown-viewer/markdown-viewer.component';
|
|
|
|
export class MyComponent {
|
|
@ViewChild(MarkdownViewerComponent) viewer?: MarkdownViewerComponent;
|
|
|
|
tocItems: { label: string; anchorId: string }[] = [];
|
|
content = '# Hello\n\nSome text.\n\n## Section Two\n\nMore text.';
|
|
|
|
scrollToHeading(event: MouseEvent, anchorId: string): void {
|
|
event.preventDefault();
|
|
this.viewer?.scrollToId(anchorId);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Content Features
|
|
|
|
### Mermaid diagrams
|
|
|
|
Fenced code blocks with language `mermaid` are automatically rendered as SVG diagrams:
|
|
|
|
````markdown
|
|
```mermaid
|
|
graph TD
|
|
A --> B
|
|
```
|
|
````
|
|
|
|
### Inline video embeds
|
|
|
|
Use the custom `!video[title](url)` syntax to embed video files (`.mp4`, `.webm`, `.ogg`) or iframes (YouTube, Vimeo, etc.):
|
|
|
|
```markdown
|
|
!video[Demo walkthrough](https://example.com/demo.mp4)
|
|
|
|
!video[YouTube video](https://www.youtube.com/embed/abc123)
|
|
```
|
|
|
|
### Tables
|
|
|
|
Standard markdown tables are styled with borders and alternating row colours automatically.
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- Content is split into sections at every heading. The text before the first heading becomes an "intro" block.
|
|
- `src` and `markdown` are mutually exclusive. If both are provided, `markdown` wins.
|
|
- `scrollToId` is a no-op if the component has not yet rendered or the anchor does not exist in the current content.
|
|
- The component is part of `AppSharedModule` and does not need to be imported separately in feature modules that already import `AppSharedModule`.
|