@mathstack/app-kit 1.1.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/.eslintrc.json +34 -0
  2. package/LICENSE +21 -21
  3. package/README.md +36 -31
  4. package/ng-package.json +15 -0
  5. package/package.json +9 -13
  6. package/project.json +76 -0
  7. package/src/lib/assets/assets-resource.ts +18 -0
  8. package/src/lib/assets/assets-service.spec.ts +54 -0
  9. package/src/lib/assets/assets-service.ts +73 -0
  10. package/src/lib/assets/index.ts +2 -0
  11. package/{baseline-styles → src/lib/baseline-styles}/_css-reset.scss +118 -118
  12. package/{baseline-styles → src/lib/baseline-styles}/_grid.scss +4 -4
  13. package/{baseline-styles → src/lib/baseline-styles}/_index.scss +6 -6
  14. package/{baseline-styles → src/lib/baseline-styles}/_sass-utils.scss +23 -23
  15. package/{baseline-styles → src/lib/baseline-styles}/_themes.scss +19 -19
  16. package/{baseline-styles → src/lib/baseline-styles}/_typography.scss +232 -232
  17. package/{baseline-styles → src/lib/baseline-styles}/_webkit-scrollbar.scss +22 -22
  18. package/{baseline-styles → src/lib/baseline-styles}/theming/_colors.scss +46 -46
  19. package/{baseline-styles → src/lib/baseline-styles}/theming/_index.scss +2 -2
  20. package/{baseline-styles → src/lib/baseline-styles}/theming/_typography.scss +32 -32
  21. package/{lib/content-parsing/index.d.ts → src/lib/content-parsing/index.ts} +2 -2
  22. package/src/lib/content-parsing/markdown-parser.ts +197 -0
  23. package/src/lib/content-parsing/shiki-highlighter.ts +219 -0
  24. package/src/lib/core/scss/_colors.scss +37 -0
  25. package/src/lib/core/scss/_functions.scss +4 -0
  26. package/src/lib/core/scss/_placeholders.scss +1 -0
  27. package/src/lib/core/scss/_vars.scss +15 -0
  28. package/src/lib/core/testing/stubs/services/assets.resource.stub.ts +3 -0
  29. package/src/lib/core/testing/stubs/services/main.service.stub.ts +5 -0
  30. package/src/lib/core/utilities/deep-merge.spec.ts +29 -0
  31. package/src/lib/core/utilities/deep-merge.ts +21 -0
  32. package/{lib/core/utilities/index.d.ts → src/lib/core/utilities/index.ts} +2 -2
  33. package/src/lib/core/utilities/safe-assign.spec.ts +41 -0
  34. package/{lib/core/utilities/safe-assign.d.ts → src/lib/core/utilities/safe-assign.ts} +42 -18
  35. package/src/lib/documentation-display/active-heading-tracker.ts +61 -0
  36. package/src/lib/documentation-display/document-index/document-index.component.html +34 -0
  37. package/src/lib/documentation-display/document-index/document-index.component.scss +56 -0
  38. package/src/lib/documentation-display/document-index/document-index.component.spec.ts +0 -0
  39. package/src/lib/documentation-display/document-index/document-index.component.ts +39 -0
  40. package/src/lib/documentation-display/documentation-config-parser.ts +92 -0
  41. package/src/lib/documentation-display/documentation-content-service.ts +118 -0
  42. package/src/lib/documentation-display/documentation-display.component.html +33 -0
  43. package/src/lib/documentation-display/documentation-display.component.scss +142 -0
  44. package/src/lib/documentation-display/documentation-display.component.spec.ts +0 -0
  45. package/src/lib/documentation-display/documentation-display.component.ts +129 -0
  46. package/{lib/documentation-display/index.d.ts → src/lib/documentation-display/index.ts} +6 -6
  47. package/src/lib/documentation-display/navigation-siblings/navigation-siblings.component.html +31 -0
  48. package/src/lib/documentation-display/navigation-siblings/navigation-siblings.component.scss +68 -0
  49. package/src/lib/documentation-display/navigation-siblings/navigation-siblings.component.spec.ts +0 -0
  50. package/src/lib/documentation-display/navigation-siblings/navigation-siblings.component.ts +52 -0
  51. package/{lib/ng-utilities/index.d.ts → src/lib/ng-utilities/index.ts} +2 -2
  52. package/src/lib/ng-utilities/ng-on-changes.spec.ts +117 -0
  53. package/src/lib/ng-utilities/ng-on-changes.ts +50 -0
  54. package/{lib/ng-utilities/run-ng-change-detection-then.d.ts → src/lib/ng-utilities/run-ng-change-detection-then.ts} +40 -22
  55. package/{public-api.d.ts → src/public-api.ts} +6 -6
  56. package/tsconfig.lib.json +16 -0
  57. package/tsconfig.lib.prod.json +11 -0
  58. package/tsconfig.spec.json +10 -0
  59. package/fesm2022/mathstack-app-kit.mjs +0 -846
  60. package/fesm2022/mathstack-app-kit.mjs.map +0 -1
  61. package/index.d.ts +0 -5
  62. package/lib/assets/assets-resource.d.ts +0 -11
  63. package/lib/assets/assets-service.d.ts +0 -35
  64. package/lib/assets/index.d.ts +0 -1
  65. package/lib/content-parsing/markdown-parser.d.ts +0 -54
  66. package/lib/content-parsing/shiki-highlighter.d.ts +0 -77
  67. package/lib/core/utilities/deep-merge.d.ts +0 -1
  68. package/lib/documentation-display/active-heading-tracker.d.ts +0 -14
  69. package/lib/documentation-display/document-index/document-index.component.d.ts +0 -17
  70. package/lib/documentation-display/documentation-config-parser.d.ts +0 -41
  71. package/lib/documentation-display/documentation-content-service.d.ts +0 -50
  72. package/lib/documentation-display/documentation-display.component.d.ts +0 -39
  73. package/lib/documentation-display/navigation-siblings/navigation-siblings.component.d.ts +0 -18
  74. package/lib/ng-utilities/ng-on-changes.d.ts +0 -22
@@ -0,0 +1,21 @@
1
+ export function deepMerge<T>(target: T, source: Partial<T>): T {
2
+ const output = Object.assign({}, target);
3
+ if (isObject(target) && isObject(source)) {
4
+ Object.keys(source).forEach((key) => {
5
+ if (isObject(source[key])) {
6
+ if (!(key in (target as object))) {
7
+ Object.assign(output, { [key]: source[key] });
8
+ } else {
9
+ output[key] = deepMerge(target[key], source[key]);
10
+ }
11
+ } else {
12
+ Object.assign(output, { [key]: source[key] });
13
+ }
14
+ });
15
+ }
16
+ return output;
17
+ }
18
+
19
+ function isObject(item: unknown): item is object {
20
+ return item && typeof item === 'object' && !Array.isArray(item);
21
+ }
@@ -1,2 +1,2 @@
1
- export * from './deep-merge';
2
- export * from './safe-assign';
1
+ export * from './deep-merge';
2
+ export * from './safe-assign';
@@ -0,0 +1,41 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { safeAssign } from './safe-assign';
3
+
4
+ describe('applyDefaultsSafely', () => {
5
+ it('should apply defaults to the target object', () => {
6
+ const target = { a: 1, b: 2 };
7
+ const defaults = { b: 3, c: 4 };
8
+
9
+ safeAssign(target, defaults);
10
+
11
+ expect(target).toEqual({ a: 1, b: 3, c: 4 } as any);
12
+ });
13
+
14
+ it('should not overwrite existing function properties', () => {
15
+ const target = { a: () => 'existing', b: 2 };
16
+ const defaults = { a: () => 'new', b: 3 };
17
+
18
+ safeAssign(target, defaults);
19
+
20
+ expect(target.a()).toBe('existing');
21
+ expect(target.b).toBe(3);
22
+ });
23
+
24
+ it('should handle empty defaults', () => {
25
+ const target = { a: 1, b: 2 };
26
+ const defaults = {};
27
+
28
+ safeAssign(target, defaults);
29
+
30
+ expect(target).toEqual({ a: 1, b: 2 });
31
+ });
32
+
33
+ it('should handle empty target', () => {
34
+ const target = {};
35
+ const defaults = { a: 1, b: 2 };
36
+
37
+ safeAssign(target, defaults);
38
+
39
+ expect(target).toEqual({ a: 1, b: 2 });
40
+ });
41
+ });
@@ -1,18 +1,42 @@
1
- /**
2
- * Safely assigns properties from a source object to a target object.
3
- *
4
- * Copies all own, enumerable properties from `source` to `target`,
5
- * except when the target already has a property of the same name that is a function.
6
- *
7
- * Logs a warning for each skipped property if `logConflicts` is true.
8
- *
9
- * @template TTarget The type of the target object.
10
- * @template TSource The type of the source object.
11
- * @param target The object to assign properties to.
12
- * @param source The object providing properties to assign.
13
- * @param logConflicts If true, logs when a method/property on the target prevents assignment. Default is false.
14
- *
15
- * @example
16
- * safeAssign(this, { log: 'hello' }, true);
17
- */
18
- export declare function safeAssign<TTarget extends object, TSource extends object>(target: TTarget, source: TSource, logConflicts?: boolean): void;
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ /**
4
+ * Safely assigns properties from a source object to a target object.
5
+ *
6
+ * Copies all own, enumerable properties from `source` to `target`,
7
+ * except when the target already has a property of the same name that is a function.
8
+ *
9
+ * Logs a warning for each skipped property if `logConflicts` is true.
10
+ *
11
+ * @template TTarget The type of the target object.
12
+ * @template TSource The type of the source object.
13
+ * @param target The object to assign properties to.
14
+ * @param source The object providing properties to assign.
15
+ * @param logConflicts If true, logs when a method/property on the target prevents assignment. Default is false.
16
+ *
17
+ * @example
18
+ * safeAssign(this, { log: 'hello' }, true);
19
+ */
20
+ export function safeAssign<TTarget extends object, TSource extends object>(
21
+ target: TTarget,
22
+ source: TSource,
23
+ logConflicts: boolean = false
24
+ ): void {
25
+ if (!source) return;
26
+
27
+ for (const key of Object.keys(source)) {
28
+ const existing = (target as any)[key];
29
+ const incoming = (source as any)[key];
30
+
31
+ if (typeof existing === 'function') {
32
+ if (logConflicts) {
33
+ console.warn(
34
+ `safeAssign: Skipped assignment for key "${key}" because it would overwrite an existing method.`
35
+ );
36
+ }
37
+ continue;
38
+ }
39
+
40
+ (target as any)[key] = incoming;
41
+ }
42
+ }
@@ -0,0 +1,61 @@
1
+ import { DestroyRef, inject, Injectable, NgZone } from '@angular/core';
2
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import {
4
+ BehaviorSubject,
5
+ debounceTime,
6
+ distinctUntilChanged,
7
+ fromEvent,
8
+ } from 'rxjs';
9
+
10
+ @Injectable({
11
+ providedIn: 'root',
12
+ })
13
+ export class AdkActiveHeadingTracker {
14
+ private activeHeadingId = new BehaviorSubject<string | null>(null);
15
+ activeHeadingId$ = this.activeHeadingId.asObservable();
16
+
17
+ private ngZone = inject(NgZone);
18
+
19
+ initScrollListener(contentEl: HTMLElement, destroyRef: DestroyRef): void {
20
+ this.ngZone.runOutsideAngular(() => {
21
+ fromEvent(window, 'scroll')
22
+ .pipe(
23
+ takeUntilDestroyed(destroyRef),
24
+ debounceTime(50),
25
+ distinctUntilChanged()
26
+ )
27
+ .subscribe(() => {
28
+ const headings = Array.from(
29
+ contentEl.querySelectorAll('h1, h2, h3, h4, h5, h6')
30
+ ) as HTMLHeadingElement[];
31
+ const activeHeading = this.findActiveHeading(headings);
32
+ this.ngZone.run(() => {
33
+ if (activeHeading) {
34
+ this.activeHeadingId.next(activeHeading.id);
35
+ }
36
+ });
37
+ });
38
+ });
39
+ }
40
+
41
+ private findActiveHeading(
42
+ headings: HTMLHeadingElement[]
43
+ ): HTMLHeadingElement | null {
44
+ const scrollPosition = window.scrollY;
45
+ for (let i = headings.length - 1; i >= 0; i--) {
46
+ const heading = headings[i];
47
+ if (heading.offsetTop <= scrollPosition + 50) {
48
+ return heading;
49
+ }
50
+ }
51
+ return headings[0] || null;
52
+ }
53
+
54
+ setActiveHeading(heading: HTMLHeadingElement): void {
55
+ this.activeHeadingId.next(heading.id);
56
+ }
57
+
58
+ scrollToTop(): void {
59
+ window.scrollTo({ top: 0, behavior: 'smooth' });
60
+ }
61
+ }
@@ -0,0 +1,34 @@
1
+ <nav class="toc">
2
+ @if (headings.length > 0) {
3
+ <p class="index-title">On this page</p>
4
+ <ul class="links-container">
5
+ @for (heading of headings; track $index) {
6
+ <li class="toc-item">
7
+ <button
8
+ type="button"
9
+ class="toc-link"
10
+ [style.--level]="heading.level"
11
+ (click)="setActiveHeading($event, heading)"
12
+ (keydown.enter)="setActiveHeading($event, heading)"
13
+ [class.active]="
14
+ heading.id === (activeHeadingTracker.activeHeadingId$ | async)
15
+ "
16
+ >{{ heading.text }}</button
17
+ >
18
+ </li>
19
+ }
20
+ </ul>
21
+ <button
22
+ type="button"
23
+ class="back-to-top"
24
+ (click)="scrollToTop()"
25
+ (keydown.enter)="scrollToTop()"
26
+ >
27
+ Back to top<span
28
+ aria-hidden="true"
29
+ class="material-symbols-outlined arrow"
30
+ >arrow_upward</span
31
+ >
32
+ </button>
33
+ }
34
+ </nav>
@@ -0,0 +1,56 @@
1
+ @use '../../baseline-styles/grid' as *;
2
+ @use '../../core/scss/placeholders' as *;
3
+ @use '../../core/scss/colors';
4
+
5
+ .links-container {
6
+ list-style: none;
7
+ flex-basis: 28%;
8
+ padding: 0;
9
+ margin: 0;
10
+ width: 250px;
11
+ }
12
+
13
+ .index-title {
14
+ @include label-2xs-caps;
15
+ padding-bottom: grid(1);
16
+ }
17
+
18
+ .toc-link {
19
+ @include body-xs;
20
+ cursor: pointer;
21
+ padding-top: grid(1);
22
+ padding-bottom: grid(1);
23
+ padding-left: calc((var(--level) - 1) * grid(4));
24
+ display: block;
25
+ border-left: 2px solid colors.$muted-primary-90;
26
+ color: colors.$muted-primary-50;
27
+ text-align: left;
28
+ }
29
+
30
+ .toc-link:hover {
31
+ color: colors.$muted-primary-25;
32
+ border-left: 2px solid colors.$muted-primary-70;
33
+ }
34
+
35
+ .toc-link.active {
36
+ color: colors.$primary-40;
37
+ border-left: 2px solid colors.$primary-40;
38
+ }
39
+
40
+ .back-to-top {
41
+ @include body-xs;
42
+ cursor: pointer;
43
+ padding: grid(2) 0;
44
+ display: flex;
45
+ align-items: center;
46
+ text-align: left;
47
+ color: colors.$muted-primary-40;
48
+ }
49
+
50
+ .back-to-top:hover {
51
+ color: colors.$muted-primary-10;
52
+ }
53
+
54
+ .arrow {
55
+ transform: scale(0.7);
56
+ }
@@ -0,0 +1,39 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import {
3
+ ChangeDetectionStrategy,
4
+ Component,
5
+ EventEmitter,
6
+ inject,
7
+ Input,
8
+ Output,
9
+ } from '@angular/core';
10
+ import { AdkActiveHeadingTracker } from '../active-heading-tracker';
11
+ import { AdkHtmlHeader } from '../documentation-content-service';
12
+
13
+ @Component({
14
+ selector: 'hsi-adk-document-index',
15
+ imports: [CommonModule],
16
+ templateUrl: './document-index.component.html',
17
+ styleUrls: ['./document-index.component.scss'],
18
+ changeDetection: ChangeDetectionStrategy.OnPush,
19
+ })
20
+ export class AdkDocumentIndexComponent {
21
+ @Input() headings: AdkHtmlHeader[] = [];
22
+ @Output() activeHeading = new EventEmitter<{
23
+ heading: AdkHtmlHeader;
24
+ event: KeyboardEvent | PointerEvent | MouseEvent | Event;
25
+ }>();
26
+
27
+ public activeHeadingTracker = inject(AdkActiveHeadingTracker);
28
+
29
+ setActiveHeading(
30
+ event: KeyboardEvent | PointerEvent | MouseEvent | Event,
31
+ heading: AdkHtmlHeader
32
+ ): void {
33
+ this.activeHeading.emit({ heading, event });
34
+ }
35
+
36
+ scrollToTop(): void {
37
+ this.activeHeadingTracker.scrollToTop();
38
+ }
39
+ }
@@ -0,0 +1,92 @@
1
+ import { Injectable } from '@angular/core';
2
+
3
+ export interface AdkNestedObject {
4
+ [key: string]: string | AdkNestedObject;
5
+ }
6
+
7
+ export interface AdkDocumentationNavigationSiblings {
8
+ previous: string | undefined;
9
+ next: string | undefined;
10
+ }
11
+
12
+ @Injectable()
13
+ export class AdkDocumentationConfigParser {
14
+ basePath: string;
15
+
16
+ /**
17
+ * This method takes a path from the application and a configuration object, and returns a path to a file that will be fetched.
18
+ *
19
+ * This assumes that the pathFromApp matches a path through the keys of the Nested Object.
20
+ *
21
+ * If the path is 'viz/content/items/bars', the config object might look like this:
22
+ * viz: {
23
+ * content: {
24
+ * items: {
25
+ * bars: 'bars.md'
26
+ * }
27
+ * }
28
+ * }
29
+ *
30
+ * @param pathFromApp a string representing the path from the application, for example 'viz/content/items/bars'
31
+ * @param config a nested object representing the configuration
32
+ * @returns a string representing the file path
33
+ */
34
+ getPathToFile(
35
+ pathFromApp: string,
36
+ config: AdkNestedObject,
37
+ basePath: string
38
+ ): string {
39
+ const pathParts = pathFromApp.split('/');
40
+ const fileName = this.getFileNameFromPath(pathParts, config);
41
+ const pathToFile = basePath ? `${basePath}/${fileName}` : fileName;
42
+ return pathToFile;
43
+ }
44
+
45
+ /**
46
+ *
47
+ * @param pathParts a string array of path parts, for example ['viz', 'content', 'items', 'bars]
48
+ * @param config a nested object representing the configuration
49
+ * @returns a string representing the file name
50
+ */
51
+ private getFileNameFromPath(
52
+ pathParts: string[],
53
+ config: string | AdkNestedObject
54
+ ): string {
55
+ const fileName = pathParts.reduce((acc, part) => {
56
+ const level = acc[part];
57
+ acc = level;
58
+ return acc;
59
+ }, config);
60
+ return fileName as string;
61
+ }
62
+
63
+ findPreviousAndNextByPath(
64
+ obj: AdkNestedObject,
65
+ targetPath: string
66
+ ): AdkDocumentationNavigationSiblings {
67
+ const flatPaths = this.flattenObjectWithPath(obj);
68
+ const index = flatPaths.indexOf(targetPath);
69
+
70
+ if (index === -1) {
71
+ return { previous: undefined, next: undefined };
72
+ }
73
+
74
+ return {
75
+ previous: index > 0 ? flatPaths[index - 1] : undefined,
76
+ next: index < flatPaths.length - 1 ? flatPaths[index + 1] : undefined,
77
+ };
78
+ }
79
+
80
+ flattenObjectWithPath(obj: AdkNestedObject, prefix: string = ''): string[] {
81
+ let result: string[] = [];
82
+ for (const [key, value] of Object.entries(obj)) {
83
+ const newPath = prefix ? `${prefix}/${key}` : key;
84
+ if (typeof value === 'object' && value !== null) {
85
+ result = result.concat(this.flattenObjectWithPath(value, newPath));
86
+ } else {
87
+ result.push(newPath);
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+ }
@@ -0,0 +1,118 @@
1
+ import { inject, Injectable } from '@angular/core';
2
+ import { map, Observable, switchMap, withLatestFrom } from 'rxjs';
3
+ import { AdkAssetResponse, AdkAssetsService } from '../assets/assets-service';
4
+ import {
5
+ AdkMarkdownParser,
6
+ AdkMarkdownParsingOptions,
7
+ AdkParsedContentSection,
8
+ } from '../content-parsing';
9
+ import {
10
+ AdkDocumentationConfigParser,
11
+ AdkDocumentationNavigationSiblings,
12
+ AdkNestedObject,
13
+ } from './documentation-config-parser';
14
+
15
+ export interface AdkDocsConfig {
16
+ title: string;
17
+ items: AdkFilesConfig;
18
+ }
19
+
20
+ export interface AdkFilesConfig {
21
+ [key: string]: AdkFilesItem;
22
+ }
23
+
24
+ export type AdkFilesItem = string | null | AdkFilesConfig;
25
+
26
+ export interface AdkParsedMarkdownFile<Content = string> {
27
+ sections: AdkParsedContentSection<Content>[];
28
+ headings: AdkHtmlHeader[];
29
+ }
30
+
31
+ export interface AdkParsedDocumentation<
32
+ Content = string,
33
+ > extends AdkParsedMarkdownFile<Content> {
34
+ siblings: AdkDocumentationNavigationSiblings;
35
+ }
36
+
37
+ export interface AdkHtmlHeader {
38
+ id: string;
39
+ level: number; // starts at 1
40
+ text: string;
41
+ }
42
+
43
+ export interface AdkDocumentationContentOptions {
44
+ fileConfig: AdkNestedObject;
45
+ basePath: string;
46
+ parsingOptions?: AdkMarkdownParsingOptions;
47
+ }
48
+
49
+ @Injectable()
50
+ export class AdkDocumentationContentService {
51
+ private files: {
52
+ [name: string]: Observable<AdkParsedContentSection[]>;
53
+ } = {};
54
+
55
+ private assetsService = inject(AdkAssetsService);
56
+ private docsConfigParser = inject(AdkDocumentationConfigParser);
57
+ private markdownParser = inject(AdkMarkdownParser);
58
+
59
+ /**
60
+ * This service requires the consuming app to provide the AdkDocumentationDisplayService, the AdkAssetsService, and the AdkDocumentationConfigParser at the level at which this service is configured.
61
+ *
62
+ * OPTIONAL: If the consuming app wants to use a different assets directory, they can pass it in here.
63
+ */
64
+ initialize(assetsDirectory?: string): void {
65
+ this.assetsService.setAssetsPath(assetsDirectory || 'assets/');
66
+ }
67
+
68
+ /**
69
+ * Assumes that the content path matches
70
+ */
71
+ getContentForCurrentContentPath(
72
+ contentPath$: Observable<string>,
73
+ options: AdkDocumentationContentOptions
74
+ ): Observable<AdkParsedDocumentation> {
75
+ return contentPath$.pipe(
76
+ switchMap((contentPath) => {
77
+ const pathToFile = this.docsConfigParser.getPathToFile(
78
+ contentPath,
79
+ options.fileConfig,
80
+ options.basePath
81
+ );
82
+ return this.getParsedMarkdownFile(pathToFile, options.parsingOptions);
83
+ }),
84
+ withLatestFrom(contentPath$),
85
+ map(([markdownSections, configPath]) => {
86
+ return {
87
+ sections: markdownSections,
88
+ headings: markdownSections.reduce((acc, section) => {
89
+ if (section.headers.length > 0) {
90
+ acc.push(...section.headers);
91
+ }
92
+ return acc;
93
+ }, [] as AdkHtmlHeader[]),
94
+ siblings: this.docsConfigParser.findPreviousAndNextByPath(
95
+ options.fileConfig,
96
+ configPath
97
+ ),
98
+ };
99
+ })
100
+ );
101
+ }
102
+
103
+ private getParsedMarkdownFile(
104
+ filePathFromAssets: string,
105
+ parsingOptions?: AdkMarkdownParsingOptions
106
+ ): Observable<AdkParsedContentSection[]> {
107
+ if (!this.files[filePathFromAssets]) {
108
+ this.files[filePathFromAssets] = this.assetsService
109
+ .getAsset(filePathFromAssets, AdkAssetResponse.Text)
110
+ .pipe(
111
+ switchMap((text) =>
112
+ this.markdownParser.parse(text as string, parsingOptions)
113
+ )
114
+ );
115
+ }
116
+ return this.files[filePathFromAssets];
117
+ }
118
+ }
@@ -0,0 +1,33 @@
1
+ <div class="documentation-container" #file>
2
+ @if (content$ | async; as content) {
3
+ <div class="documentation-content">
4
+ <div class="main-content">
5
+ @for (section of content.sections; track $index) {
6
+ @if (section.type === 'markdown') {
7
+ <div class="injected-html" [innerHTML]="section.html"></div>
8
+ } @else if (section.type === 'escaped') {
9
+ @if (escapedContentTemplateRef) {
10
+ <ng-container
11
+ [ngTemplateOutlet]="escapedContentTemplateRef"
12
+ [ngTemplateOutletContext]="{ $implicit: section }"
13
+ >
14
+ </ng-container>
15
+ }
16
+ }
17
+ }
18
+ @if (showPrevNextNavigation) {
19
+ <hsi-adk-navigation-siblings
20
+ class="nav-siblings"
21
+ [siblings]="content.siblings"
22
+ (nextDoc)="navigateToDoc($event)"
23
+ ></hsi-adk-navigation-siblings>
24
+ }
25
+ </div>
26
+ <hsi-adk-document-index
27
+ class="document-index"
28
+ [headings]="content.headings.slice(1)"
29
+ (activeHeading)="scrollToHeading($event)"
30
+ ></hsi-adk-document-index>
31
+ </div>
32
+ }
33
+ </div>